arcane-os 0.71.2 → 0.73.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +25 -0
- package/README.md +1 -1
- package/docs/reference/README.md +5 -4
- package/docs/reference/core-native-packaging.md +74 -7
- package/docs/reference/core-shared-host.md +43 -4
- package/docs/reference/inventory/package-api.json +37 -9
- package/docs/reference/pwa.md +60 -0
- package/docs/reference/sdk-api.md +147 -26
- package/package.json +1 -1
- package/schemas/arcane-app.schema.json +4 -0
- package/src/app-descriptor.mjs +7 -1
- package/src/core/host.mjs +73 -13
- package/src/core/hosts/macos/ArcaneLauncher.m +14 -3
- package/src/core/hosts/windows/ArcaneLauncher.cs +25 -5
- package/src/dev-server.mjs +14 -21
- package/src/index.mjs +1 -0
- package/src/native/portable-layout.mjs +9 -1
- package/src/native/portable-provider.mjs +1 -0
- package/src/pwa-retirement.mjs +53 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,30 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.73.0
|
|
4
|
+
|
|
5
|
+
- Add public `resolvePwaRetirementResponse({workspaceRoot, appId})` for existing
|
|
6
|
+
custom Node hosts. It supplies the shared retirement worker response only for
|
|
7
|
+
an explicitly disabled standalone PWA whose authored root worker is absent.
|
|
8
|
+
- Preserve raw `enabled: false` selection before descriptor normalization in
|
|
9
|
+
custom-host and SDK source serving. Omitted settings, enabled PWAs, authored
|
|
10
|
+
workers and other routes keep their existing behavior.
|
|
11
|
+
- Retire only the worker's own registration through the existing browser
|
|
12
|
+
lifecycle. Saved data, caches, DBOPFS and open pages remain untouched; no
|
|
13
|
+
forced reload, polling or replacement server is introduced.
|
|
14
|
+
|
|
15
|
+
## 0.72.0
|
|
16
|
+
|
|
17
|
+
- Add optional `native.launchContext` defaults for ordinary packaged launch and
|
|
18
|
+
public `resolveNativeLaunchContext()` through `arcane-os/core/host`. Native
|
|
19
|
+
applications and external MCP can select the same app-owned state, workspace,
|
|
20
|
+
shared endpoint and log without reconstructing platform paths.
|
|
21
|
+
- Extend `readCoreLaunchContext()` with explicit defaults and opt-in native
|
|
22
|
+
resolution. Preserve complete explicit launch records, selected workspace
|
|
23
|
+
values, ordinary stdio ownership and the existing no-option entry path.
|
|
24
|
+
- Forward packaged launch defaults through Windows and macOS launchers while
|
|
25
|
+
preserving window, profile, service and shared-host lifecycle behavior. No
|
|
26
|
+
stored-data migration or dependency change is introduced.
|
|
27
|
+
|
|
3
28
|
## 0.71.2
|
|
4
29
|
|
|
5
30
|
- Preserve complete browser Kokoro synthesis input through the selected runtime's
|
package/README.md
CHANGED
|
@@ -20,7 +20,7 @@ selected SDK projection or an explicit live SDK source mount. Both profiles
|
|
|
20
20
|
share the theme, packaging, event, cancellation,
|
|
21
21
|
and browser run contracts while retaining their selected application layout.
|
|
22
22
|
|
|
23
|
-
This checkout defines the `0.
|
|
23
|
+
This checkout defines the `0.73.0` SDK contract. Development applications declare
|
|
24
24
|
`arcane-os` as `latest` and retain their lockfile. Exact SDK pins begin when Roshi
|
|
25
25
|
designates a production build. Follow [Consumer updates](docs/consumer-updates.md)
|
|
26
26
|
to refresh and compare the installed SDK and lockfile with the published channel;
|
package/docs/reference/README.md
CHANGED
|
@@ -43,6 +43,7 @@ alone does not make bare module names resolve in a browser.
|
|
|
43
43
|
| Use the `arcane` command | [CLI reference](cli.md) |
|
|
44
44
|
| Install an app and cache selected resources offline | [Progressive web applications](pwa.md) |
|
|
45
45
|
| Disable source PWA generation or explicitly retire confirmed generated output | [Source PWA retirement](pwa.md#disable-source-pwa-generation-and-retire-generated-output) |
|
|
46
|
+
| Retire the prior worker after explicitly disabling standalone source PWA | [Disabled-worker browser lifecycle](pwa.md#disabled-worker-retirement-on-the-source-server) |
|
|
46
47
|
| Generate named browser imports or inspect the selected physical runtime | [`arcane import-map`](cli.md#arcane-import-map) and [browser runtime delivery](protocols.md#browser-runtime-delivery) |
|
|
47
48
|
| Connect an application through the shared WebSocket transport | [Shared WebSocket clients](websocket-client.md) |
|
|
48
49
|
| Choose browser, native, cloud, or cross-host behavior | [Availability and normalization](availability-and-normalization.md) |
|
|
@@ -53,7 +54,7 @@ alone does not make bare module names resolve in a browser.
|
|
|
53
54
|
| Choose theme-switcher corner radii, label font and group background through host CSS | [Theme-switcher styling properties](runtime-components.md#theme-switcherhtml) |
|
|
54
55
|
| Use optional Core preferences and appearance without masking real failures | [PreferenceStore](runtime-modules.md#preferencestorejs), [SystemAppearance](runtime-modules.md#systemappearancejs), and [Core lookup errors](core-runtime.md#state-and-frames) |
|
|
55
56
|
| Submit complete speech parts immediately and play them in order | [`SpeechPlayback`](runtime-modules.md#speechplaybackjs) and the [basic browser example](ai/browser-speech.md#play-a-complete-array-with-speechplayback) |
|
|
56
|
-
| Preserve exact text
|
|
57
|
+
| Preserve exact speech text and complete browser Kokoro audio | [Exact complete synthesis input](ai/browser-speech.md#exact-complete-synthesis-input) and [managed AI narration](sdk-api.md#managed-ai-narration) |
|
|
57
58
|
| Select initial native window dimensions and resizing behavior | [Initial native window size](core-native-packaging.md#initial-native-window-size) and [Windows provider](sdk-api.md#createwindowsnativeprovider) |
|
|
58
59
|
| Call `globalThis.Arcane` | [Arcane Core API](core/arcane-api.md) |
|
|
59
60
|
| Connect a browser to one Core transport | [Core browser client](core-client.md) |
|
|
@@ -61,8 +62,8 @@ alone does not make bare module names resolve in a browser.
|
|
|
61
62
|
| Compose native Core dispatch, service lifecycle, and stdio hosting | [Native Core runtime](core-runtime.md) |
|
|
62
63
|
| Reuse dependency-ready native services without a renderer | [Core service composition](core-runtime.md#native-service-composition) and [existing ONNX owner](local-ai.md#native-service-owners) |
|
|
63
64
|
| Compose application Core services during source development, independently of local AI | [Development Core services](core-development.md) |
|
|
64
|
-
| Share one application service runtime between its native
|
|
65
|
-
| Store complete browser model resources through one DBOPFS owner | [Model resources](ai/browser-decisions.md#shared-dbopfs-model-resources) and [speech routing](ai/browser-speech.md#ordinary-module-routing) |
|
|
65
|
+
| Share one application service runtime between its ordinary native launch and MCP process | [Shared Core host](core-shared-host.md) and [native launch defaults](core-native-packaging.md#launch-time-locations) |
|
|
66
|
+
| Store complete browser model resources, including the selected decision-runtime entry, through one DBOPFS owner | [Model resources](ai/browser-decisions.md#shared-dbopfs-model-resources) and [speech routing](ai/browser-speech.md#ordinary-module-routing) |
|
|
66
67
|
| Observe committed storage changes across live application documents | [DBOPFS change subscriptions](runtime-modules.md#dbopfsjs) |
|
|
67
68
|
| Cancel cold speech preparation while preserving other activation interests | [Prepared narration](ai/browser-speech.md#prepare-narration-once-and-replay-stored-audio) |
|
|
68
69
|
| Own an explicitly selected native Codex App Server session | [Codex App Server](codex-app-server.md) |
|
|
@@ -85,7 +86,7 @@ alone does not make bare module names resolve in a browser.
|
|
|
85
86
|
| Write, commit and push complete selected repository text | [Selected-text repository writing](core-repositories.md#write-commit-and-push-selected-text) |
|
|
86
87
|
| Observe existing checkout configuration and capture an explicit remote/ref | [Repository configuration and selected targets](core-repositories.md#existing-checkout-configuration-and-selected-targets) |
|
|
87
88
|
| Build an installed-SDK portable Core payload | [Portable Core packaging](core-native-packaging.md) |
|
|
88
|
-
|
|
|
89
|
+
| Resolve matching native state/workspace/endpoint defaults and preserve explicit launch values | [Launch-time locations](core-native-packaging.md#launch-time-locations) and [resolveNativeLaunchContext()](sdk-api.md#resolvenativelaunchcontext) |
|
|
89
90
|
| Build a Windows executable without an OS checkout | [Windows executable packaging](core-native-packaging.md#windows-executable) |
|
|
90
91
|
| Compose a Mac application with an architecture-matched host | [macOS application composition](core-native-packaging.md#macos-application-composition) |
|
|
91
92
|
| Subscribe to native events | [Arcane event reference](core/arcane-events.md) |
|
|
@@ -210,6 +210,64 @@ details and [the client contract](core-client.md) for native bridge integration.
|
|
|
210
210
|
|
|
211
211
|
## Launch-time locations
|
|
212
212
|
|
|
213
|
+
An application can select defaults for ordinary double-click launch in its
|
|
214
|
+
descriptor, without a replacement launcher or a required command-line file:
|
|
215
|
+
|
|
216
|
+
```json
|
|
217
|
+
{
|
|
218
|
+
"native": {
|
|
219
|
+
"launchContext": {
|
|
220
|
+
"sharedHost": {},
|
|
221
|
+
"dispatch": {"destination": "Moon Cheese Receiving Dock"}
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
This is the `native` fragment of the application's complete descriptor. The
|
|
228
|
+
optional `native.launchContext` object is copied intact to the portable
|
|
229
|
+
manifest and generated Core entry. Its presence explicitly selects native
|
|
230
|
+
location defaults. `sharedHost:{}` opts into the shared Core endpoint and log;
|
|
231
|
+
omitting `sharedHost` retains stdio-owned Core lifetime. Omitting
|
|
232
|
+
`native.launchContext` retains the previous generated-entry behavior entirely.
|
|
233
|
+
Windows and macOS launchers use the same packaged defaults for native state
|
|
234
|
+
selection, so an ordinary executable launch and external MCP can select the
|
|
235
|
+
same app-owned Core.
|
|
236
|
+
|
|
237
|
+
`resolveNativeLaunchContext({appId,context={}})` from `arcane-os/core/host` is
|
|
238
|
+
the public, synchronous location owner for native applications and external MCP.
|
|
239
|
+
It returns a new complete context and fills only omitted fields:
|
|
240
|
+
|
|
241
|
+
| Field | SDK default |
|
|
242
|
+
|---|---|
|
|
243
|
+
| `stateRoot` on Windows | `%LOCALAPPDATA%/Arcane/<appId>`; standard home `AppData/Local` fallback when the environment value is absent |
|
|
244
|
+
| `stateRoot` on macOS | `~/Library/Application Support/Arcane/<appId>` |
|
|
245
|
+
| `stateRoot` on Linux | Absolute `$XDG_DATA_HOME/Arcane/<appId>`, otherwise `~/.local/share/Arcane/<appId>` |
|
|
246
|
+
| `workspaceRoot` | `<resolved stateRoot>/Workspace` |
|
|
247
|
+
| `sharedHost.endpoint`, only when `sharedHost` is selected | Windows named pipe `\\.\pipe\Arcane-` followed by the URL-encoded complete resolved state path; Linux/macOS socket `<resolved stateRoot>/core.sock` |
|
|
248
|
+
| `sharedHost.logFile`, only when `sharedHost` is selected | `<resolved stateRoot>/Core.log` |
|
|
249
|
+
|
|
250
|
+
Windows/macOS roots retain the existing native host locations. Linux is the
|
|
251
|
+
portable Node host convention; an external composing host supplies its actual
|
|
252
|
+
state root when it selects another location. Android and other host adaptations
|
|
253
|
+
must supply `stateRoot` explicitly and own native IPC/process adaptation.
|
|
254
|
+
The resolver never reads saved preferences, creates directories, changes the
|
|
255
|
+
environment, starts services, or migrates existing data. The generated opted-in
|
|
256
|
+
shared entry creates its selected state directory because its socket/log need
|
|
257
|
+
that parent; application services continue to own workspace I/O. Custom
|
|
258
|
+
endpoint/log parent directories retain the existing shared-host requirements.
|
|
259
|
+
|
|
260
|
+
Explicit `stateRoot`, `workspaceRoot`, endpoint, log filename and additional
|
|
261
|
+
fields remain authored values; the resolver derives omitted paths from the
|
|
262
|
+
absolute selected state location. Relative explicit paths retain the caller's
|
|
263
|
+
working-directory meaning, so UI and MCP callers must select the same location.
|
|
264
|
+
No hashing, app-local OS path composition, directory scanning, or new repository
|
|
265
|
+
location rule is involved. `resolveArcaneDataPaths()` continues to own its
|
|
266
|
+
separate `ArcaneData/Repos` contract. A selected native context needs a nonempty
|
|
267
|
+
string `appId` and state directory; a selected `sharedHost` must be an object.
|
|
268
|
+
Malformed resolver inputs report `TypeError`; ordinary OS endpoint requirements
|
|
269
|
+
remain at the IPC operation.
|
|
270
|
+
|
|
213
271
|
For one app-owned Core shared by the native window and a separate MCP process,
|
|
214
272
|
the launch context may explicitly select `sharedHost:{endpoint,logFile}`. The
|
|
215
273
|
generated native child then connects through a stdio bridge, starting an
|
|
@@ -224,27 +282,36 @@ or `null`; the shared host replays that current listener to a newly attached
|
|
|
224
282
|
macOS bridge, without restarting the server or retaining historical events.
|
|
225
283
|
|
|
226
284
|
The generated Core entry accepts `--arcane-launch-config <path>`. The public
|
|
227
|
-
`readCoreLaunchContext({argv=process.argv.slice(2)}={})`
|
|
228
|
-
`arcane-os/core/host` reads that explicitly selected JSON object.
|
|
285
|
+
`readCoreLaunchContext({argv=process.argv.slice(2),defaults={},appId}={})`
|
|
286
|
+
function in `arcane-os/core/host` reads that explicitly selected JSON object.
|
|
287
|
+
Precedence is packaged `defaults`, then the native host's actual state-root
|
|
288
|
+
argument, then every complete top-level field from the explicit JSON object.
|
|
289
|
+
Nested records replace their default field as a whole; they are not deep-merged.
|
|
290
|
+
An `appId` explicitly opts the result into `resolveNativeLaunchContext`; the
|
|
291
|
+
generated entry supplies it only when `native.launchContext` is present.
|
|
292
|
+
A native host
|
|
229
293
|
can separately supply `--arcane-host-state-root <directory>` for the reader's
|
|
230
294
|
`stateRoot` default. Every field in the explicit JSON object takes precedence
|
|
231
295
|
unchanged, including an explicitly present `stateRoot` with a relative, null or
|
|
232
|
-
other application-owned value
|
|
296
|
+
other application-owned value on the ordinary non-resolving reader. With neither
|
|
297
|
+
argument nor defaults/appId the reader still returns `{}`.
|
|
233
298
|
Missing files, missing argument values and malformed JSON report their actual
|
|
234
299
|
errors; the reader does not search for configuration, resolve or validate the
|
|
235
|
-
state directory, rewrite the file or change the process environment.
|
|
300
|
+
state directory, rewrite the file or change the process environment. The
|
|
301
|
+
explicitly opted-in resolver supplies its documented defaults after this merge.
|
|
236
302
|
|
|
237
303
|
The generated entry supplies an artifact-derived `appRoot` and then applies the
|
|
238
304
|
complete explicit launch context. Every service factory receives that same
|
|
239
305
|
object as its second argument, preserving its authored first argument. A
|
|
240
306
|
launcher can supply the user's selected `workspaceRoot` and writable
|
|
241
307
|
`stateRoot` at launch instead of embedding this machine's paths in an app
|
|
242
|
-
package.
|
|
243
|
-
|
|
308
|
+
package. Explicit product selections take precedence over the opted-in SDK
|
|
309
|
+
location defaults. The SDK chooses no saved preference filename or model. JSON values and additional
|
|
244
310
|
application-owned fields remain unchanged, including an app-selected
|
|
245
311
|
`preferencesFile`. The state directory supplies no preference filename and
|
|
246
312
|
triggers no stored-data discovery or migration. Linux and other composing hosts
|
|
247
|
-
may pass the same separate argument; the reader chooses no platform
|
|
313
|
+
may pass the same separate argument; the ordinary reader chooses no platform
|
|
314
|
+
directory until native resolution is explicitly selected.
|
|
248
315
|
|
|
249
316
|
## Selected local-AI runtimes
|
|
250
317
|
|
|
@@ -146,8 +146,46 @@ events may change; applications own those domain methods and state.
|
|
|
146
146
|
|
|
147
147
|
## Native launch and platforms
|
|
148
148
|
|
|
149
|
-
The generated portable Core entry keeps ordinary stdio behavior unless
|
|
150
|
-
|
|
149
|
+
The generated portable Core entry keeps ordinary stdio behavior unless its
|
|
150
|
+
selected launch context contains `sharedHost`. An application can opt in for
|
|
151
|
+
ordinary packaged executable launch with `native.launchContext:{sharedHost:{}}`
|
|
152
|
+
in its complete descriptor. The SDK supplies the app's platform state root,
|
|
153
|
+
workspace, endpoint and diagnostic log through `resolveNativeLaunchContext`.
|
|
154
|
+
The same public owner is available to external MCP:
|
|
155
|
+
|
|
156
|
+
```js
|
|
157
|
+
import {readCoreLaunchContext, connectSharedCoreHost} from 'arcane-os/core/host';
|
|
158
|
+
|
|
159
|
+
const context = await readCoreLaunchContext({
|
|
160
|
+
appId: appDescriptor.id,
|
|
161
|
+
defaults: appDescriptor.native.launchContext
|
|
162
|
+
});
|
|
163
|
+
const connection = await connectSharedCoreHost({
|
|
164
|
+
endpoint: context.sharedHost.endpoint
|
|
165
|
+
});
|
|
166
|
+
// Invoke the application's actual services through connection.client.
|
|
167
|
+
// Disconnect this MCP client without shutting down the application's host.
|
|
168
|
+
await connection.close();
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
This example connects to a running native/headless host. The existing `start`
|
|
172
|
+
option can start the packaged Core entry explicitly when independent MCP-first
|
|
173
|
+
startup is needed; the entry applies those same packaged defaults. Pass the
|
|
174
|
+
same explicit `--arcane-launch-config` file to both entries for user-selected
|
|
175
|
+
locations. A caller that already owns a complete context can use the pure
|
|
176
|
+
`resolveNativeLaunchContext({appId,context})` directly instead of reading argv.
|
|
177
|
+
For MCP-first startup, prepare the returned `context.stateRoot` with Node's
|
|
178
|
+
`mkdir(context.stateRoot,{recursive:true})` before selecting `start`, because
|
|
179
|
+
the parent's startup diagnostic log opens before the headless child runs.
|
|
180
|
+
This uses the resolved directory directly, without duplicating platform path
|
|
181
|
+
rules. Pass `context.sharedHost.logFile` to that existing `start` record and
|
|
182
|
+
the same launch file to the packaged Core command. Custom endpoint/log parent
|
|
183
|
+
directories remain caller-selected.
|
|
184
|
+
See [launch-time locations](core-native-packaging.md#launch-time-locations) for
|
|
185
|
+
the exact platform defaults, precedence, location override and directory rules.
|
|
186
|
+
No app must reconstruct OS directory rules.
|
|
187
|
+
|
|
188
|
+
An explicit launch file can also select the existing full endpoint contract:
|
|
151
189
|
|
|
152
190
|
```json
|
|
153
191
|
{
|
|
@@ -163,8 +201,9 @@ option, the native Core child is a stdio-to-shared-Core bridge. It connects or
|
|
|
163
201
|
launches the same entry with `--arcane-core-headless`; the headless entry claims
|
|
164
202
|
the endpoint before importing services. Closing the native window ends its
|
|
165
203
|
bridge, while the independent Core host remains available. Service options and
|
|
166
|
-
the complete launch context retain their existing values.
|
|
167
|
-
|
|
204
|
+
the complete launch context retain their existing values. Shared mode remains
|
|
205
|
+
explicit: the new descriptor option selects defaults only for that application,
|
|
206
|
+
and the existing no-option stdio path is unchanged.
|
|
168
207
|
|
|
169
208
|
`startSharedCoreBridge({endpoint,start,input=process.stdin,
|
|
170
209
|
output=process.stdout,signal,onError})` is also public. It returns
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"schemaVersion": 1,
|
|
3
|
-
"sdkVersion": "0.
|
|
3
|
+
"sdkVersion": "0.72.0",
|
|
4
4
|
"environment": {
|
|
5
5
|
"runtime": "Node.js for Node entrypoints; browser for browser-only entrypoints",
|
|
6
6
|
"minimumVersion": "22.23.2 for Node entrypoints",
|
|
7
7
|
"moduleSystem": "ESM"
|
|
8
8
|
},
|
|
9
|
-
"memberCount":
|
|
9
|
+
"memberCount": 299,
|
|
10
10
|
"runtimeSubpathPatterns": {
|
|
11
11
|
"arcane-os/modules/*": "./runtime/arcane/modules/*",
|
|
12
12
|
"arcane-os/entities/*": "./runtime/arcane/entities/*"
|
|
@@ -542,7 +542,7 @@
|
|
|
542
542
|
"summary": "Explicit-load Laya and Julia-1 decisions with caller-selected precision, complete inputs and graph results.",
|
|
543
543
|
"availability": "Browser module Workers and the selected Transformers.js backend; import/construction starts no model load",
|
|
544
544
|
"protocol": "Caller-owned state/question/options rows; five-input decision graph; native Worker request/result lifecycle",
|
|
545
|
-
"normalization": "Full field tokenization, raw logits and unrounded softmax; dtype defaults to fp16 for Laya and fp32 for Julia, with explicit values forwarded unchanged. Optional existing SDK store routes actual loader resources through fetchResource outside cloned Worker configuration;
|
|
545
|
+
"normalization": "Full field tokenization, raw logits and unrounded softmax; dtype defaults to fp16 for Laya and fp32 for Julia, with explicit values forwarded unchanged. Optional existing SDK store routes the complete selected runtime entry and actual loader resources through fetchResource outside cloned Worker configuration. Each activation materializes unchanged entry source with JavaScript MIME and retains its original configured URL for later loads; its object URL is revoked after Worker termination. Stored mode disables competing model caches, preserves complete HTTP responses and joins runtime-entry/resource cleanup before outstanding calls settle. Runtime-entry HTTP failures retain error.response. Custom stored entries must support an object URL and changed import.meta.url; native absolute dependencies and custom factory imports outside upstream preloads remain browser-owned. Omission retains direct native module and upstream model loading; no decision-result persistence or precision fallback"
|
|
546
546
|
},
|
|
547
547
|
{
|
|
548
548
|
"id": "root:acceptWebSocket",
|
|
@@ -1185,7 +1185,7 @@
|
|
|
1185
1185
|
],
|
|
1186
1186
|
"primaryImport": "arcane-os",
|
|
1187
1187
|
"group": "Headless toolchain operations",
|
|
1188
|
-
"summary": "Starts one owned browser development server for the selected application.",
|
|
1188
|
+
"summary": "Starts one owned browser development server for the selected application, including native worker retirement when standalone source PWA is explicitly disabled and its root worker is absent.",
|
|
1189
1189
|
"availability": "Node; selected operation may produce browser or native output",
|
|
1190
1190
|
"protocol": "Shared headless operation API + arcane-cli-events/1",
|
|
1191
1191
|
"normalization": "SDK-normalized inputs, errors, events, and documented result"
|
|
@@ -2208,6 +2208,20 @@
|
|
|
2208
2208
|
"protocol": "arcane-target-adapter/1, arcane-native-build-plan/1, or provider protocol",
|
|
2209
2209
|
"normalization": "Normalized complete plan and result; target-specific artifact detail preserved"
|
|
2210
2210
|
},
|
|
2211
|
+
{
|
|
2212
|
+
"id": "root:resolvePwaRetirementResponse",
|
|
2213
|
+
"name": "resolvePwaRetirementResponse",
|
|
2214
|
+
"displayName": "resolvePwaRetirementResponse()",
|
|
2215
|
+
"kind": "function",
|
|
2216
|
+
"signature": "async resolvePwaRetirementResponse({workspaceRoot=process.cwd(), appId}={})",
|
|
2217
|
+
"entrypoints": ["arcane-os"],
|
|
2218
|
+
"primaryImport": "arcane-os",
|
|
2219
|
+
"group": "Workspace, doctor, repository, and server",
|
|
2220
|
+
"summary": "Resolves the shared disabled-PWA root worker response for an existing custom source host, preserving authored workers and browser saved data.",
|
|
2221
|
+
"availability": "Node; browser service-worker lifecycle remains browser-owned",
|
|
2222
|
+
"protocol": "Node ESM and native service-worker lifecycle",
|
|
2223
|
+
"normalization": "Current standalone descriptor and absent-root-worker selection; complete status/headers/body or null"
|
|
2224
|
+
},
|
|
2211
2225
|
{
|
|
2212
2226
|
"id": "root:resolveWorkspace",
|
|
2213
2227
|
"name": "resolveWorkspace",
|
|
@@ -2446,7 +2460,7 @@
|
|
|
2446
2460
|
],
|
|
2447
2461
|
"primaryImport": "arcane-os",
|
|
2448
2462
|
"group": "Workspace, doctor, repository, and server",
|
|
2449
|
-
"summary": "Starts an owned source or packaged browser server with complete selected routes, HTTPS by default, explicit HTTP selection, listener readiness and shutdown.",
|
|
2463
|
+
"summary": "Starts an owned source or packaged browser server with complete selected routes, HTTPS by default, explicit HTTP selection, listener readiness and shutdown; explicitly disabled standalone source PWA retires an absent root worker through its native lifecycle.",
|
|
2450
2464
|
"availability": "Node control plane; browser data plane",
|
|
2451
2465
|
"protocol": "Node ESM",
|
|
2452
2466
|
"normalization": "SDK-normalized inputs, errors, events, and documented result"
|
|
@@ -3708,7 +3722,7 @@
|
|
|
3708
3722
|
"summary": "Creates a local-only Kokoro text-to-speech provider for the provider-neutral AI runtime.",
|
|
3709
3723
|
"availability": "Browser with DBOPFS, Web Locks, Workers, object URLs, and caller-selected kokoro-js runtime/model content",
|
|
3710
3724
|
"protocol": "arcane-ai-provider/2 tts/synthesize",
|
|
3711
|
-
"normalization": "Independent load, status, request, unload, cancellation and disposal; optional ordinary model.voices records remain complete in the synchronous catalog without loading or constraining synthesis. Provider-native requests return 24000 Hz Float32Array PCM, while shared AI.js requests use wav and return complete Uint8Array audio/wav data; never selects a cloud fallback"
|
|
3725
|
+
"normalization": "Independent load, status, request, unload, cancellation and disposal; optional ordinary model.voices records remain complete in the synchronous catalog without loading or constraining synthesis. Upstream text normalization and phonemization remain selected-runtime behavior. Tokenization disables truncation; short inputs use one inference and longer inputs send every content token through ordered engine-capacity segments, retaining required outer padding and all generated audio in order. Segments share the slot's existing model and disposal owner. Request-local cancellation is observed between segments and before returning audio; an already-running upstream inference may finish. Provider-native requests return 24000 Hz Float32Array PCM, while shared AI.js requests use wav and return complete Uint8Array audio/wav data; never selects a cloud fallback"
|
|
3712
3726
|
},
|
|
3713
3727
|
{
|
|
3714
3728
|
"id": "browser-speech:createBrowserSpeechArtifactGraph",
|
|
@@ -4530,19 +4544,33 @@
|
|
|
4530
4544
|
"protocol": "arcane/1 preferences methods and schemaVersion 1 entries file",
|
|
4531
4545
|
"normalization": "Binding identity is exactly equal to the named createPreferencesService export"
|
|
4532
4546
|
},
|
|
4547
|
+
{
|
|
4548
|
+
"id": "core-host:resolveNativeLaunchContext",
|
|
4549
|
+
"name": "resolveNativeLaunchContext",
|
|
4550
|
+
"displayName": "resolveNativeLaunchContext()",
|
|
4551
|
+
"kind": "function",
|
|
4552
|
+
"signature": "resolveNativeLaunchContext({appId,context={}}={})",
|
|
4553
|
+
"entrypoints": ["arcane-os/core/host"],
|
|
4554
|
+
"primaryImport": "arcane-os/core/host",
|
|
4555
|
+
"group": "Native Core host composition",
|
|
4556
|
+
"summary": "Resolves matching per-app native state, workspace and optional shared endpoint defaults for packaged UI and external MCP.",
|
|
4557
|
+
"availability": "Node native locations on Windows/Linux/macOS; Android and other hosts supply stateRoot and native adaptation",
|
|
4558
|
+
"protocol": "Pure native launch-context resolution and existing local IPC",
|
|
4559
|
+
"normalization": "Returns a new complete context with only omitted stateRoot/workspaceRoot and selected sharedHost endpoint/log fields filled; preserves explicit values and additional payloads. Native launchContext presence opts generated entry into resolution; omitted sharedHost retains stdio. No I/O, saved-state discovery, process startup, migration or ArcaneData/Repos change. Malformed selected appId/stateRoot/sharedHost inputs report TypeError; platform IPC errors remain operation-owned."
|
|
4560
|
+
},
|
|
4533
4561
|
{
|
|
4534
4562
|
"id": "core-host:readCoreLaunchContext",
|
|
4535
4563
|
"name": "readCoreLaunchContext",
|
|
4536
4564
|
"displayName": "readCoreLaunchContext()",
|
|
4537
4565
|
"kind": "function",
|
|
4538
|
-
"signature": "async readCoreLaunchContext({argv=process.argv.slice(2)}={})",
|
|
4566
|
+
"signature": "async readCoreLaunchContext({argv=process.argv.slice(2),defaults={},appId}={})",
|
|
4539
4567
|
"entrypoints": ["arcane-os/core/host"],
|
|
4540
4568
|
"primaryImport": "arcane-os/core/host",
|
|
4541
4569
|
"group": "Native Core host composition",
|
|
4542
|
-
"summary": "
|
|
4570
|
+
"summary": "Combines packaged defaults, actual native host stateRoot and complete explicit launch JSON, with optional native location resolution.",
|
|
4543
4571
|
"availability": "Node, including the selected external-entry SEA launcher",
|
|
4544
4572
|
"protocol": "Explicit launch arguments and JSON configuration",
|
|
4545
|
-
"normalization": "
|
|
4573
|
+
"normalization": "Precedence is defaults, then --arcane-host-state-root, then complete top-level --arcane-launch-config JSON fields; nested values replace without deep merging or payload rewriting. Explicit appId invokes resolveNativeLaunchContext; ordinary calls retain relative/null/custom state values and return an empty record with no inputs. Missing arguments, file/JSON errors and malformed records reject. No configuration search, file rewrite, migration or environment mutation; generated native.launchContext explicitly selects the resolver."
|
|
4546
4574
|
},
|
|
4547
4575
|
{
|
|
4548
4576
|
"id": "core-host:startCoreHost",
|
package/docs/reference/pwa.md
CHANGED
|
@@ -207,6 +207,66 @@ The server path uses the existing portable Node serving owner, and browser
|
|
|
207
207
|
retirement requires the browser's native service-worker support and an existing
|
|
208
208
|
registration eligible for that update.
|
|
209
209
|
|
|
210
|
+
### Retirement through an existing custom host
|
|
211
|
+
|
|
212
|
+
An application that already serves its source through `node-http-server` can
|
|
213
|
+
keep that server and delegate only its root worker route to the public SDK
|
|
214
|
+
representation owner:
|
|
215
|
+
|
|
216
|
+
```js
|
|
217
|
+
import {Server} from 'node-http-server';
|
|
218
|
+
import {resolvePwaRetirementResponse} from 'arcane-os';
|
|
219
|
+
|
|
220
|
+
// This is the application's existing subclass and workspace root.
|
|
221
|
+
class DragonMailServer extends Server {
|
|
222
|
+
async onRequest(request, response) {
|
|
223
|
+
const pathname = new URL(request.originalUrl || request.url, 'http://localhost').pathname;
|
|
224
|
+
if ((request.method === 'GET' || request.method === 'HEAD') && pathname === '/arcane-sw.js') {
|
|
225
|
+
const retirement = await resolvePwaRetirementResponse({
|
|
226
|
+
workspaceRoot: this.workspaceRoot,
|
|
227
|
+
appId: 'dragon-mail'
|
|
228
|
+
});
|
|
229
|
+
if (retirement) {
|
|
230
|
+
response.statusCode = retirement.statusCode;
|
|
231
|
+
for (const [name, value] of Object.entries(retirement.headers)) {
|
|
232
|
+
response.setHeader(name, value);
|
|
233
|
+
}
|
|
234
|
+
await this.serve(request, response, retirement.body);
|
|
235
|
+
return true;
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
// Keep the application's other routes here before ordinary static serving.
|
|
239
|
+
return false;
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
Use the application's actual root and ID. The SDK reads the current
|
|
245
|
+
`arcane-packager.json` and, for standalone `appsRoot:"."`, the authored root
|
|
246
|
+
`arcane-app.json`; only an absent authored descriptor selects
|
|
247
|
+
`arcane-package.json`. Reads use the existing descriptor projection and package
|
|
248
|
+
contract without rewriting either file or requiring a stale generated projection
|
|
249
|
+
to equal a newly edited authored descriptor. `appId` is optional; when supplied,
|
|
250
|
+
it must match that descriptor's ID.
|
|
251
|
+
|
|
252
|
+
The result is `null` for multi-app workspaces, omitted or enabled PWA, an omitted
|
|
253
|
+
`pwa.enabled` field (including `{pwa:{}}`), and any existing root `arcane-sw.js`
|
|
254
|
+
entry. The author's explicit `pwa.enabled:false` choice and an `ENOENT`
|
|
255
|
+
worker result produce `{statusCode, headers, body}`: status `200`, the shared
|
|
256
|
+
JavaScript content type, `Cache-Control: no-cache`, and the complete existing
|
|
257
|
+
retirement script. Malformed configuration and other filesystem failures reject
|
|
258
|
+
instead of selecting another descriptor or treating a file as absent.
|
|
259
|
+
|
|
260
|
+
The host applies those headers directly; it must not replace `no-cache` with
|
|
261
|
+
its unrelated response defaults or treat a former enabled worker's validator as
|
|
262
|
+
validation of this response. The existing `serve()` method owns response
|
|
263
|
+
completion and HEAD body omission. Keep this exact worker route at the serving
|
|
264
|
+
origin that owns the registration; application HTTPS redirects and other routes
|
|
265
|
+
remain application-owned. This API starts no server, writes no file, performs
|
|
266
|
+
no offline-inventory scan, and touches no browser data. It shares the same
|
|
267
|
+
response policy and worker generator as the SDK source server. The native
|
|
268
|
+
activation and existing-page lifetime limits above also apply here.
|
|
269
|
+
|
|
210
270
|
### Authored host root and app entry
|
|
211
271
|
|
|
212
272
|
An authored `index.html` at the source host root keeps `/` separate from the
|
|
@@ -57,7 +57,7 @@ runtime layouts.
|
|
|
57
57
|
| `arcane-os/core/runtime` | App-neutral native Core dispatcher and service lifecycle. |
|
|
58
58
|
| `arcane-os/core/development` | App-service composition and document-owned Core transport during source development, independently of local AI. |
|
|
59
59
|
| `arcane-os/core/stdio` | Framed Node stdio transport with graceful runtime drain. |
|
|
60
|
-
| `arcane-os/core/host` | Node-only reusable Core host lifecycle
|
|
60
|
+
| `arcane-os/core/host` | Node-only reusable Core host lifecycle, app-selected native launch defaults and shared native location resolution. |
|
|
61
61
|
| `arcane-os/codex/app-server` | Explicit native Codex App Server session, complete live turns, server requests and recovery. |
|
|
62
62
|
| `arcane-os/mcp/stdio` | Node MCP STDIO server for app-owned tools and static resources. |
|
|
63
63
|
| `arcane-os/core/repositories` | Native persistent ArcaneData paths, connected working repositories and scoped non-secret Git identity configuration. |
|
|
@@ -429,7 +429,8 @@ The remaining data-only subpaths are eight JSON Schemas and package metadata.
|
|
|
429
429
|
| `connectSharedCoreHost()` | function | `arcane-os/core/host` | Shared Core host | Node local IPC and optional explicit headless startup |
|
|
430
430
|
| `runSharedCoreHost()` | function | `arcane-os/core/host` | Shared Core host | Node headless entry and optional parent startup IPC |
|
|
431
431
|
| `startSharedCoreBridge()` | function | `arcane-os/core/host` | Shared Core host | Node readable/writable streams and local IPC |
|
|
432
|
-
| `readCoreLaunchContext()` | function | `arcane-os/core/host` | Native Core host composition | Node;
|
|
432
|
+
| `readCoreLaunchContext()` | function | `arcane-os/core/host` | Native Core host composition | Node; combines packaged defaults, host state and explicitly selected launch JSON |
|
|
433
|
+
| `resolveNativeLaunchContext()` | function | `arcane-os/core/host` | Native Core host composition | Node native paths; Android supplies its adapted state root |
|
|
433
434
|
| `resolveArcaneDataPaths()` | function | `arcane-os/core/repositories` | Native repository workspaces | Node on Windows, Linux and macOS; Android supplies its data root |
|
|
434
435
|
| `createRepositoryWorkspace()` | function | `arcane-os/core/repositories` | Native repository workspaces | Node with Git; Android requires host process adaptation |
|
|
435
436
|
| `readGitIdentity()` | function | `arcane-os/core/repositories` | Native repository workspaces | Node with Git; Android requires host process adaptation |
|
|
@@ -4205,6 +4206,39 @@ There is no history scan, app loop, diagnostic payload store or automatic retry.
|
|
|
4205
4206
|
The authored `test/websocket.test.mjs` fixtures cover this boundary; source
|
|
4206
4207
|
inspection is distinct from executing them or verifying a browser deployment.
|
|
4207
4208
|
|
|
4209
|
+
## resolvePwaRetirementResponse()
|
|
4210
|
+
|
|
4211
|
+
```text
|
|
4212
|
+
async resolvePwaRetirementResponse({workspaceRoot=process.cwd(), appId}={})
|
|
4213
|
+
```
|
|
4214
|
+
|
|
4215
|
+
Import from `arcane-os`. This Node API supplies the shared disabled-PWA worker
|
|
4216
|
+
response to an application's existing server without starting or replacing it.
|
|
4217
|
+
It reads the current standalone workspace configuration and root authored
|
|
4218
|
+
descriptor, using the package descriptor only when the authored file is absent.
|
|
4219
|
+
The optional `appId` must match the selected descriptor. It uses the existing
|
|
4220
|
+
configuration contracts, performs no writes and rereads current configuration
|
|
4221
|
+
on each call; there is no retained cache, timer, subscription or dispose handle.
|
|
4222
|
+
|
|
4223
|
+
It returns `null` for a non-standalone workspace, omitted or enabled PWA, an
|
|
4224
|
+
omitted `pwa.enabled` field, or an existing root `arcane-sw.js` entry. The author's
|
|
4225
|
+
explicit `pwa.enabled:false` choice plus an `ENOENT` root
|
|
4226
|
+
worker produces `{statusCode:200, headers, body}`, with the existing JavaScript
|
|
4227
|
+
content type, `Cache-Control: no-cache`, and the complete retirement script.
|
|
4228
|
+
Configuration/JSON errors and other filesystem failures reject. The result
|
|
4229
|
+
carries no prior worker `Last-Modified` validator.
|
|
4230
|
+
|
|
4231
|
+
The caller owns its exact GET/HEAD `/arcane-sw.js` route and applies the supplied
|
|
4232
|
+
status, headers and body through its existing `node-http-server` public
|
|
4233
|
+
`serve()` path. A `null` result leaves normal routing intact. There is no request
|
|
4234
|
+
payload transformation, server listener, generated file, cache/storage cleanup,
|
|
4235
|
+
client enumeration or reload. The worker skips waiting on installation and
|
|
4236
|
+
unregisters its own registration on activation; existing-page controller timing
|
|
4237
|
+
remains browser-owned. The [custom-host PWA guide](pwa.md#retirement-through-an-existing-custom-host)
|
|
4238
|
+
shows the complete hook and preservation contract. The Node filesystem path is
|
|
4239
|
+
portable across Windows, Linux and macOS; Android needs its normal Node host
|
|
4240
|
+
adaptation, and browser retirement requires native service-worker support.
|
|
4241
|
+
|
|
4208
4242
|
## startDevServer()
|
|
4209
4243
|
|
|
4210
4244
|
### Overview
|
|
@@ -7925,29 +7959,90 @@ The default binding from `arcane-os/core/diarization` is exactly the named
|
|
|
7925
7959
|
import createDiarizationService from 'arcane-os/core/diarization';
|
|
7926
7960
|
```
|
|
7927
7961
|
|
|
7962
|
+
## resolveNativeLaunchContext()
|
|
7963
|
+
|
|
7964
|
+
### Overview
|
|
7965
|
+
|
|
7966
|
+
Resolves one application's native state, workspace and optional shared endpoint
|
|
7967
|
+
through the same public owner used by its packaged executable and external MCP.
|
|
7968
|
+
This synchronous function performs no I/O, saved-state discovery, process start
|
|
7969
|
+
or migration.
|
|
7970
|
+
|
|
7971
|
+
### Signature and result
|
|
7972
|
+
|
|
7973
|
+
```text
|
|
7974
|
+
resolveNativeLaunchContext({appId,context={}}={})
|
|
7975
|
+
```
|
|
7976
|
+
|
|
7977
|
+
Import from `arcane-os/core/host`. The result is a new complete context with
|
|
7978
|
+
omitted fields filled. Windows defaults to `%LOCALAPPDATA%/Arcane/<appId>`
|
|
7979
|
+
(home `AppData/Local` fallback), macOS to
|
|
7980
|
+
`~/Library/Application Support/Arcane/<appId>`, and portable Linux Node to
|
|
7981
|
+
absolute `$XDG_DATA_HOME/Arcane/<appId>` or `~/.local/share/Arcane/<appId>`.
|
|
7982
|
+
Android and other adapted hosts provide their actual `stateRoot` and own native
|
|
7983
|
+
IPC/process adaptation.
|
|
7984
|
+
|
|
7985
|
+
An omitted `workspaceRoot` becomes `<absolute selected stateRoot>/Workspace`.
|
|
7986
|
+
Only a selected `sharedHost` object receives a default endpoint and log file:
|
|
7987
|
+
Windows uses a named pipe containing the URL-encoded complete absolute state
|
|
7988
|
+
path; Linux/macOS use `<absolute selected stateRoot>/core.sock`. The log is
|
|
7989
|
+
`<absolute selected stateRoot>/Core.log`. Explicit fields, additional payloads
|
|
7990
|
+
and explicit workspace values remain unchanged. Relative explicit paths retain
|
|
7991
|
+
the caller's working-directory meaning. `appId` and the selected state path
|
|
7992
|
+
must be nonempty strings; a selected `sharedHost` must be an object. Malformed
|
|
7993
|
+
inputs report `TypeError`; native endpoint failures belong to the IPC operation.
|
|
7994
|
+
|
|
7995
|
+
```javascript
|
|
7996
|
+
import {resolveNativeLaunchContext} from 'arcane-os/core/host';
|
|
7997
|
+
|
|
7998
|
+
const context = resolveNativeLaunchContext({
|
|
7999
|
+
appId: 'moon-cheese-post',
|
|
8000
|
+
context: {sharedHost: {}}
|
|
8001
|
+
});
|
|
8002
|
+
// Use context.sharedHost.endpoint for the native UI and external MCP.
|
|
8003
|
+
// An explicit context.workspaceRoot or context.stateRoot remains selected.
|
|
8004
|
+
```
|
|
8005
|
+
|
|
8006
|
+
In a complete app descriptor, `native.launchContext:{sharedHost:{}}` selects
|
|
8007
|
+
these defaults for ordinary packaged executable launch. Omitting `sharedHost`
|
|
8008
|
+
keeps stdio-owned lifetime; omitting `native.launchContext` preserves the prior
|
|
8009
|
+
generated-entry path. The selected shared entry creates its state directory
|
|
8010
|
+
for socket/log use; application services own workspace I/O. MCP-first startup
|
|
8011
|
+
prepares that returned state directory before the existing `start` option opens
|
|
8012
|
+
its log. No caller needs to reconstruct platform paths. The separate
|
|
8013
|
+
`resolveArcaneDataPaths()` repository-location contract remains unchanged.
|
|
8014
|
+
See [launch-time locations](core-native-packaging.md#launch-time-locations) and
|
|
8015
|
+
[shared native/MCP hosting](core-shared-host.md#native-launch-and-platforms).
|
|
8016
|
+
|
|
7928
8017
|
## readCoreLaunchContext()
|
|
7929
8018
|
|
|
7930
8019
|
### Overview
|
|
7931
8020
|
|
|
7932
|
-
|
|
7933
|
-
|
|
7934
|
-
|
|
8021
|
+
Combines complete packaged defaults, a separately supplied native host state
|
|
8022
|
+
directory and the complete JSON object selected by `--arcane-launch-config`.
|
|
8023
|
+
An explicit `appId` selects the native resolver; ordinary calls remain a
|
|
8024
|
+
configuration reader without automatic location selection.
|
|
7935
8025
|
|
|
7936
8026
|
### Signature and result
|
|
7937
8027
|
|
|
7938
8028
|
```text
|
|
7939
|
-
async readCoreLaunchContext({argv=process.argv.slice(2)}={})
|
|
8029
|
+
async readCoreLaunchContext({argv=process.argv.slice(2),defaults={},appId}={})
|
|
7940
8030
|
```
|
|
7941
8031
|
|
|
7942
|
-
Import
|
|
7943
|
-
|
|
7944
|
-
|
|
7945
|
-
|
|
7946
|
-
|
|
7947
|
-
|
|
7948
|
-
|
|
7949
|
-
|
|
7950
|
-
|
|
8032
|
+
Import from `arcane-os/core/host`. Precedence is `defaults`, then
|
|
8033
|
+
`--arcane-host-state-root <directory>`, then every complete top-level field from
|
|
8034
|
+
the explicit launch JSON object. Nested fields replace rather than deep-merge.
|
|
8035
|
+
The original file and its payload remain unchanged. Supplying `appId` applies
|
|
8036
|
+
`resolveNativeLaunchContext` to that merged record; its defaults and input
|
|
8037
|
+
contract then apply. Without `appId`, relative, null and other explicit state
|
|
8038
|
+
values retain the prior reader semantics. With no arguments or defaults, the
|
|
8039
|
+
ordinary call still resolves to `{}`.
|
|
8040
|
+
|
|
8041
|
+
Missing argument values, unreadable files, malformed JSON, non-object defaults
|
|
8042
|
+
or launch records reject. Relative launch-file paths use the working directory.
|
|
8043
|
+
The reader searches no other configuration, rewrites no content, migrates no
|
|
8044
|
+
data and changes no environment. Application-owned fields such as
|
|
8045
|
+
`preferencesFile` retain their complete values.
|
|
7951
8046
|
|
|
7952
8047
|
```javascript
|
|
7953
8048
|
import {readCoreLaunchContext} from 'arcane-os/core/host';
|
|
@@ -7956,12 +8051,13 @@ const launchContext = await readCoreLaunchContext();
|
|
|
7956
8051
|
```
|
|
7957
8052
|
|
|
7958
8053
|
The generated entry supplies artifact-derived `appRoot`, then applies the whole
|
|
7959
|
-
|
|
7960
|
-
|
|
7961
|
-
|
|
7962
|
-
|
|
7963
|
-
|
|
7964
|
-
|
|
8054
|
+
context and passes the same object as every service factory's second argument.
|
|
8055
|
+
Authored service options remain the first argument. It supplies the descriptor's
|
|
8056
|
+
`native.launchContext` as `defaults` and the app ID only when that optional
|
|
8057
|
+
field is present. Matching Windows and macOS launchers apply the same packaged
|
|
8058
|
+
defaults before explicit launch JSON and forward their actual state directory;
|
|
8059
|
+
other composing hosts can use the same public seam. Compiled host availability
|
|
8060
|
+
and native execution are separate from this JavaScript contract. See
|
|
7965
8061
|
[launch-time locations](core-native-packaging.md#launch-time-locations).
|
|
7966
8062
|
|
|
7967
8063
|
## resolveArcaneDataPaths()
|
|
@@ -9550,13 +9646,26 @@ No decisions are inserted into chat history or durable storage.
|
|
|
9550
9646
|
|
|
9551
9647
|
Optional `store` is an existing SDK DBOPFS model or speech store exposing
|
|
9552
9648
|
`fetchResource(input, options)`. It remains outside cloned Worker configuration.
|
|
9553
|
-
|
|
9554
|
-
|
|
9555
|
-
|
|
9556
|
-
|
|
9557
|
-
|
|
9649
|
+
Explicit activation first stores the complete selected `runtime.moduleUrl`
|
|
9650
|
+
entry and materializes it as a JavaScript object URL without rewriting source.
|
|
9651
|
+
The configured original URL remains the resource selection for later loads.
|
|
9652
|
+
The selected loader also routes its actual model/configuration/tokenizer
|
|
9653
|
+
resources through that owner and disables competing model caches in this mode.
|
|
9654
|
+
Each activation revokes its runtime object URL after terminating its Worker on
|
|
9655
|
+
cancellation, failure, unload or disposal. Resource progress counts closed
|
|
9656
|
+
shards; cancellation joins runtime-entry and resource cleanup before outstanding
|
|
9657
|
+
`load()`/`evaluate()` calls settle. Runtime-entry HTTP failures report
|
|
9658
|
+
`ARCANE_DECISION_RUNTIME_DOWNLOAD_FAILED` with the complete stored response in
|
|
9659
|
+
`error.response` for developer diagnostics.
|
|
9558
9660
|
`unload()` and `dispose()` acknowledge their state transition synchronously.
|
|
9559
9661
|
|
|
9662
|
+
The default runtime entry is self-contained. Custom stored entries must work
|
|
9663
|
+
from an object URL: entry materialization changes `import.meta.url` and does not
|
|
9664
|
+
rewrite relative imports or original-location support-file references. Native
|
|
9665
|
+
absolute ESM dependencies and custom factory imports outside upstream preloads
|
|
9666
|
+
remain browser-owned. Omitting `store` preserves direct native module loading
|
|
9667
|
+
and ordinary upstream model loading. See [shared model resources](ai/browser-decisions.md#shared-dbopfs-model-resources).
|
|
9668
|
+
|
|
9560
9669
|
### Example
|
|
9561
9670
|
|
|
9562
9671
|
```js
|
|
@@ -10442,6 +10551,18 @@ mono 16-bit PCM. Unsupported formats fail
|
|
|
10442
10551
|
`ARCANE_AI_UNSUPPORTED_RESPONSE_FORMAT`; malformed adapter audio fails
|
|
10443
10552
|
`ARCANE_AI_INVALID_PROVIDER_RESULT`. Unknown fields and accessors reject as malformed.
|
|
10444
10553
|
|
|
10554
|
+
The Kokoro adapter retains upstream text normalization and phonemization, then
|
|
10555
|
+
tokenizes without truncation and sends every content token through the selected
|
|
10556
|
+
engine. Short inputs use one inference; longer inputs use ordered segments
|
|
10557
|
+
within that engine's capacity and return all generated audio in order. Each
|
|
10558
|
+
segment retains the engine's required outer padding, voice-style selection and
|
|
10559
|
+
speed. The segments share the slot's existing loaded model and disposal owner;
|
|
10560
|
+
they do not create another model or provider-pool slot. Cancellation is checked
|
|
10561
|
+
between inference segments and before returning audio; already-running upstream
|
|
10562
|
+
inference may finish. This engine transport is distinct from the SDK's optional
|
|
10563
|
+
`textFormat:'plain'` input treatment. See [complete synthesis input](ai/browser-speech.md#exact-complete-synthesis-input)
|
|
10564
|
+
for the selected tokenizer/model contract and upstream revision behavior.
|
|
10565
|
+
|
|
10445
10566
|
Automatic execution tries a complete WebNN NPU pool when
|
|
10446
10567
|
`navigator.ml.createContext` is exposed, then WebGPU when `navigator.gpu` is
|
|
10447
10568
|
exposed, then CPU through WASM. A failed candidate is cleaned up before a fresh
|
package/package.json
CHANGED
|
@@ -132,6 +132,10 @@
|
|
|
132
132
|
}
|
|
133
133
|
},
|
|
134
134
|
"localAI": { "$ref": "#/$defs/localAI" },
|
|
135
|
+
"launchContext": {
|
|
136
|
+
"type": "object",
|
|
137
|
+
"description": "Optional complete native launch defaults. Presence opts into SDK per-app native locations; sharedHost:{} explicitly selects the shared endpoint. Explicit launch JSON fields take precedence unchanged."
|
|
138
|
+
},
|
|
135
139
|
"window": {
|
|
136
140
|
"type": "object",
|
|
137
141
|
"additionalProperties": false,
|
package/src/app-descriptor.mjs
CHANGED
|
@@ -254,7 +254,7 @@ function validateNativeWindow(value){
|
|
|
254
254
|
}
|
|
255
255
|
|
|
256
256
|
function validateNative(value,appId){
|
|
257
|
-
assertOnlyKeys(value,new Set(['type','icon','order','bundledApps','documentCatalog','localAI','services','window']),'descriptor.native');
|
|
257
|
+
assertOnlyKeys(value,new Set(['type','icon','order','bundledApps','documentCatalog','localAI','services','window','launchContext']),'descriptor.native');
|
|
258
258
|
if(!NATIVE_TYPES.has(value.type))fail('descriptor.native.type is unsupported.');
|
|
259
259
|
const icon=value.icon===null?null:normalizeRelativePath(value.icon,'descriptor.native.icon');
|
|
260
260
|
if(!is.integer(value.order)||value.order<0){
|
|
@@ -265,6 +265,9 @@ function validateNative(value,appId){
|
|
|
265
265
|
});
|
|
266
266
|
const documentCatalog=validateDocumentCatalog(value.documentCatalog);
|
|
267
267
|
const window=validateNativeWindow(value.window);
|
|
268
|
+
if(value.launchContext!==undefined&&!isObject(value.launchContext)){
|
|
269
|
+
fail('descriptor.native.launchContext must be an object.');
|
|
270
|
+
}
|
|
268
271
|
let services;
|
|
269
272
|
if(value.services!==undefined){
|
|
270
273
|
if(!is.array(value.services))fail('descriptor.native.services must be an array.');
|
|
@@ -279,6 +282,7 @@ function validateNative(value,appId){
|
|
|
279
282
|
order:value.order,
|
|
280
283
|
bundledApps,
|
|
281
284
|
...(window===undefined?{}:{window}),
|
|
285
|
+
...(value.launchContext===undefined?{}:{launchContext:value.launchContext}),
|
|
282
286
|
...(services===undefined?{}:{services}),
|
|
283
287
|
...(value.localAI===undefined?{}:{localAI:normalizeLocalAIConfig(value.localAI)}),
|
|
284
288
|
...(documentCatalog?{documentCatalog}:{})
|
|
@@ -416,6 +420,7 @@ export function projectNativeDescriptor(descriptor,{source}={}){
|
|
|
416
420
|
...(value.native.documentCatalog?{documentCatalog:value.native.documentCatalog}:{}),
|
|
417
421
|
...(value.native.services===undefined?{}:{services:value.native.services}),
|
|
418
422
|
...(value.native.window===undefined?{}:{window:value.native.window}),
|
|
423
|
+
...(value.native.launchContext===undefined?{}:{launchContext:value.native.launchContext}),
|
|
419
424
|
...(value.native.localAI===undefined?{}:{localAI:value.native.localAI}),
|
|
420
425
|
include:[...value.package.include]
|
|
421
426
|
};
|
|
@@ -462,6 +467,7 @@ function synthesizedDescriptor(packageManifest,nativeDescriptor){
|
|
|
462
467
|
bundledApps:[...(native.bundledApps??[])],
|
|
463
468
|
...(native.services===undefined?{}:{services:native.services}),
|
|
464
469
|
...(native.window===undefined?{}:{window:native.window}),
|
|
470
|
+
...(native.launchContext===undefined?{}:{launchContext:native.launchContext}),
|
|
465
471
|
...((packageManifest.localAI??native.localAI)===undefined?{}:{localAI:packageManifest.localAI??native.localAI}),
|
|
466
472
|
...(native.documentCatalog?{documentCatalog:native.documentCatalog}:{})
|
|
467
473
|
},
|
package/src/core/host.mjs
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import {readFile} from 'node:fs/promises';
|
|
2
|
+
import {homedir} from 'node:os';
|
|
3
|
+
import path from 'node:path';
|
|
2
4
|
import Is from 'strong-type';
|
|
3
5
|
import {createCoreRuntime} from './runtime.mjs';
|
|
4
6
|
import {startCoreStdio} from './stdio.mjs';
|
|
@@ -7,32 +9,90 @@ export {startSharedCoreHost, connectSharedCoreHost, runSharedCoreHost, startShar
|
|
|
7
9
|
|
|
8
10
|
const is = new Is(false);
|
|
9
11
|
|
|
12
|
+
/** Resolve one app's native locations without creating or migrating data. */
|
|
13
|
+
export function resolveNativeLaunchContext({appId, context = {}} = {}) {
|
|
14
|
+
if (!is.string(appId) || !appId) throw new TypeError('Native launch resolution requires an appId.');
|
|
15
|
+
if (context === null || !is.object(context) || is.array(context)) {
|
|
16
|
+
throw new TypeError('Native launch context must be an object.');
|
|
17
|
+
}
|
|
18
|
+
let stateRoot = context.stateRoot;
|
|
19
|
+
if (stateRoot === undefined) {
|
|
20
|
+
const home = homedir();
|
|
21
|
+
switch (process.platform) {
|
|
22
|
+
case 'win32':
|
|
23
|
+
stateRoot = path.join(process.env.LOCALAPPDATA || path.join(home, 'AppData', 'Local'), 'Arcane', appId);
|
|
24
|
+
break;
|
|
25
|
+
case 'darwin':
|
|
26
|
+
stateRoot = path.join(home, 'Library', 'Application Support', 'Arcane', appId);
|
|
27
|
+
break;
|
|
28
|
+
case 'linux': {
|
|
29
|
+
const xdg = process.env.XDG_DATA_HOME;
|
|
30
|
+
stateRoot = path.join(xdg && path.isAbsolute(xdg) ? xdg : path.join(home, '.local', 'share'), 'Arcane', appId);
|
|
31
|
+
break;
|
|
32
|
+
}
|
|
33
|
+
default:
|
|
34
|
+
throw new TypeError('This native host must supply its persistent stateRoot.');
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
if (!is.string(stateRoot) || !stateRoot) throw new TypeError('Native stateRoot must be a nonempty directory path.');
|
|
38
|
+
const root = path.resolve(stateRoot);
|
|
39
|
+
const resolved = {
|
|
40
|
+
...context,
|
|
41
|
+
stateRoot,
|
|
42
|
+
workspaceRoot: context.workspaceRoot === undefined ? path.join(root, 'Workspace') : context.workspaceRoot
|
|
43
|
+
};
|
|
44
|
+
if (context.sharedHost !== undefined) {
|
|
45
|
+
if (context.sharedHost === null || !is.object(context.sharedHost) || is.array(context.sharedHost)) {
|
|
46
|
+
throw new TypeError('Native sharedHost must be an object.');
|
|
47
|
+
}
|
|
48
|
+
// Encode the complete selected Windows path as a pipe-name component;
|
|
49
|
+
// Unix IPC uses an ordinary file in that same persistent app directory.
|
|
50
|
+
const endpoint = process.platform === 'win32'
|
|
51
|
+
? '\\\\.\\pipe\\Arcane-' + encodeURIComponent(root)
|
|
52
|
+
: path.join(root, 'core.sock');
|
|
53
|
+
resolved.sharedHost = {
|
|
54
|
+
...context.sharedHost,
|
|
55
|
+
endpoint: context.sharedHost.endpoint === undefined ? endpoint : context.sharedHost.endpoint,
|
|
56
|
+
logFile: context.sharedHost.logFile === undefined ? path.join(root, 'Core.log') : context.sharedHost.logFile
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
return resolved;
|
|
60
|
+
}
|
|
61
|
+
|
|
10
62
|
/**
|
|
11
|
-
*
|
|
63
|
+
* Packaged defaults precede the host's actual state-root argument.
|
|
12
64
|
* Every field in an explicitly selected launch JSON object takes precedence
|
|
13
65
|
* unchanged; field meanings belong to the launch owner and individual services.
|
|
66
|
+
* appId explicitly selects native location resolution; ordinary calls keep the
|
|
67
|
+
* existing read-only launch-file contract without choosing any locations.
|
|
14
68
|
*/
|
|
15
|
-
export async function readCoreLaunchContext({argv = process.argv.slice(2)} = {}) {
|
|
69
|
+
export async function readCoreLaunchContext({argv = process.argv.slice(2), defaults = {}, appId} = {}) {
|
|
70
|
+
if (defaults === null || !is.object(defaults) || is.array(defaults)) {
|
|
71
|
+
throw new TypeError('Core launch defaults must be an object.');
|
|
72
|
+
}
|
|
16
73
|
const stateFlag = argv.indexOf('--arcane-host-state-root');
|
|
17
|
-
const
|
|
74
|
+
const context = {...defaults};
|
|
18
75
|
if (stateFlag !== -1) {
|
|
19
76
|
const stateRoot = argv[stateFlag + 1];
|
|
20
77
|
if (stateRoot === undefined || stateRoot === '') {
|
|
21
78
|
throw new TypeError('--arcane-host-state-root requires a directory.');
|
|
22
79
|
}
|
|
23
|
-
|
|
80
|
+
context.stateRoot = stateRoot;
|
|
24
81
|
}
|
|
25
82
|
const flag = argv.indexOf('--arcane-launch-config');
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
83
|
+
let selected = context;
|
|
84
|
+
if (flag !== -1) {
|
|
85
|
+
const filename = argv[flag + 1];
|
|
86
|
+
if (filename === undefined || filename === '') {
|
|
87
|
+
throw new TypeError('--arcane-launch-config requires a filename.');
|
|
88
|
+
}
|
|
89
|
+
const explicit = JSON.parse(await readFile(filename, 'utf8'));
|
|
90
|
+
if (explicit === null || !is.object(explicit) || is.array(explicit)) {
|
|
91
|
+
throw new TypeError('Core launch configuration must be a JSON object.');
|
|
92
|
+
}
|
|
93
|
+
selected = {...context, ...explicit};
|
|
34
94
|
}
|
|
35
|
-
return {
|
|
95
|
+
return appId === undefined ? selected : resolveNativeLaunchContext({appId, context: selected});
|
|
36
96
|
}
|
|
37
97
|
|
|
38
98
|
/** Compose app services with the shared dispatcher and draining transport. */
|
|
@@ -231,14 +231,25 @@ typedef NS_ENUM(NSUInteger, ArcaneDiagnosticStream) {
|
|
|
231
231
|
NSString *appID = ArcaneRequiredString(app, @"id", error);
|
|
232
232
|
if (!appID) return NO;
|
|
233
233
|
_title = [app[@"displayName"] isKindOfClass:NSString.class] ? app[@"displayName"] : appID;
|
|
234
|
+
id defaults = manifest[@"launchContext"];
|
|
235
|
+
if (defaults && ![defaults isKindOfClass:NSDictionary.class]) {
|
|
236
|
+
*error = ArcaneLauncherError(@"CORE_LAUNCH_MANIFEST_INVALID", @"The native launchContext must be an object.", defaults);
|
|
237
|
+
return NO;
|
|
238
|
+
}
|
|
239
|
+
NSMutableDictionary *launchContext = defaults ? [defaults mutableCopy] : [NSMutableDictionary dictionary];
|
|
234
240
|
if (_launchFilename) {
|
|
235
|
-
|
|
236
|
-
if (!
|
|
241
|
+
NSDictionary *explicitContext = ArcaneReadRecord([NSURL fileURLWithPath:_launchFilename], error);
|
|
242
|
+
if (!explicitContext) return NO;
|
|
243
|
+
[launchContext addEntriesFromDictionary:explicitContext];
|
|
237
244
|
}
|
|
245
|
+
_launchContext = launchContext;
|
|
246
|
+
// The opted-in Node resolver uses HOME when supplied; retain the previous
|
|
247
|
+
// native default unchanged for applications without launchContext.
|
|
248
|
+
NSString *nativeHome = defaults ? (NSProcessInfo.processInfo.environment[@"HOME"] ?: NSHomeDirectory()) : NSHomeDirectory();
|
|
238
249
|
id selectedState = _launchContext[@"stateRoot"];
|
|
239
250
|
_stateRoot = [selectedState isKindOfClass:NSString.class]
|
|
240
251
|
? ArcaneAbsolutePath(selectedState, _workingDirectory)
|
|
241
|
-
: [[
|
|
252
|
+
: [[nativeHome stringByAppendingPathComponent:@"Library/Application Support/Arcane"] stringByAppendingPathComponent:appID];
|
|
242
253
|
[self scheduleDiagnostics];
|
|
243
254
|
NSString *clientPath = ArcaneRequiredString(client, @"source", error);
|
|
244
255
|
if (!clientPath) return NO;
|
|
@@ -57,13 +57,33 @@ namespace Arcane.Core.Hosts.Windows
|
|
|
57
57
|
Dictionary<string, object> host = manifest.TryGetValue("host", out hostValue) ? hostValue as Dictionary<string, object> : null;
|
|
58
58
|
string icon = host != null && host.TryGetValue("icon", out iconValue) ? iconValue as string : null;
|
|
59
59
|
|
|
60
|
-
//
|
|
61
|
-
//
|
|
62
|
-
Dictionary<string, object> launchContext =
|
|
60
|
+
// Native UI and generated Core use the same packaged defaults.
|
|
61
|
+
// Core rereads the explicit file; every explicit field wins.
|
|
62
|
+
Dictionary<string, object> launchContext = manifest.ContainsKey("launchContext")
|
|
63
|
+
? new Dictionary<string, object>(RequiredRecord(manifest, "launchContext"))
|
|
64
|
+
: new Dictionary<string, object>();
|
|
65
|
+
if (launchFilename != null)
|
|
66
|
+
{
|
|
67
|
+
foreach (KeyValuePair<string, object> field in ReadRecord(launchFilename))
|
|
68
|
+
launchContext[field.Key] = field.Value;
|
|
69
|
+
}
|
|
70
|
+
string defaultStateBase = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData);
|
|
71
|
+
if (manifest.ContainsKey("launchContext"))
|
|
72
|
+
{
|
|
73
|
+
// Match the public Node resolver for this opt-in contract,
|
|
74
|
+
// including an explicitly selected process environment.
|
|
75
|
+
defaultStateBase = Environment.GetEnvironmentVariable("LOCALAPPDATA");
|
|
76
|
+
if (String.IsNullOrEmpty(defaultStateBase))
|
|
77
|
+
{
|
|
78
|
+
string home = Environment.GetEnvironmentVariable("USERPROFILE");
|
|
79
|
+
if (String.IsNullOrEmpty(home)) home = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile);
|
|
80
|
+
defaultStateBase = Path.Combine(home, "AppData", "Local");
|
|
81
|
+
}
|
|
82
|
+
}
|
|
63
83
|
object stateRoot;
|
|
64
|
-
string profileParent = launchContext
|
|
84
|
+
string profileParent = launchContext.TryGetValue("stateRoot", out stateRoot) && stateRoot is string
|
|
65
85
|
? Path.GetFullPath((string)stateRoot)
|
|
66
|
-
: Path.Combine(
|
|
86
|
+
: Path.Combine(defaultStateBase, "Arcane", appId);
|
|
67
87
|
OpenDiagnostics(Path.Combine(profileParent, "Diagnostics"));
|
|
68
88
|
ArcaneHostOptions options = new ArcaneHostOptions
|
|
69
89
|
{
|
package/src/dev-server.mjs
CHANGED
|
@@ -17,7 +17,7 @@ import {
|
|
|
17
17
|
readWorkspaceAssetVersion,resolveAssetReference,rewriteAssetReferences,versionAssetUrl
|
|
18
18
|
} from './import-map.mjs';
|
|
19
19
|
import {createPwaArtifacts,selectPwaFiles} from './pwa.mjs';
|
|
20
|
-
import {
|
|
20
|
+
import {createPwaRetirementResponse} from './pwa-retirement.mjs';
|
|
21
21
|
|
|
22
22
|
const is = new Is(false);
|
|
23
23
|
|
|
@@ -1074,6 +1074,7 @@ async function startOwnedDevServer({
|
|
|
1074
1074
|
currentSourceRoutes = {
|
|
1075
1075
|
...routeSet,
|
|
1076
1076
|
app: manifest,
|
|
1077
|
+
pwaExplicitlyDisabled: (authored ? value.package?.pwa : value.pwa)?.enabled === false,
|
|
1077
1078
|
rootDocument,
|
|
1078
1079
|
startPath: applicationSourcePath(routeSet.config,routeSet.appId,manifest.entry),
|
|
1079
1080
|
mappings: mappings.map(
|
|
@@ -1303,26 +1304,18 @@ async function startOwnedDevServer({
|
|
|
1303
1304
|
return;
|
|
1304
1305
|
}
|
|
1305
1306
|
if (mode === 'source' && selectedRoutes.config.appsRoot === '.'
|
|
1306
|
-
&& selectedRoutes.
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
}
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
request,
|
|
1319
|
-
response,
|
|
1320
|
-
{
|
|
1321
|
-
// A former enabled worker's date cannot validate this retirement script.
|
|
1322
|
-
contentType: contentTypes.js,
|
|
1323
|
-
body: createPwaRetirementWorkerScript
|
|
1324
|
-
}
|
|
1325
|
-
);
|
|
1307
|
+
&& selectedRoutes.pwaExplicitlyDisabled && target.path === '/arcane-sw.js') {
|
|
1308
|
+
const retirement = await createPwaRetirementResponse({
|
|
1309
|
+
workspaceRoot: selectedRoutes.workspaceRoot,
|
|
1310
|
+
pwa: selectedRoutes.app.pwa
|
|
1311
|
+
});
|
|
1312
|
+
if (retirement) {
|
|
1313
|
+
response.statusCode = retirement.statusCode;
|
|
1314
|
+
for (const [name, value] of Object.entries(retirement.headers)) {
|
|
1315
|
+
response.setHeader(name, value);
|
|
1316
|
+
}
|
|
1317
|
+
// A former enabled worker's date cannot validate this retirement script.
|
|
1318
|
+
await fileServer.serve(request, response, retirement.body);
|
|
1326
1319
|
return;
|
|
1327
1320
|
}
|
|
1328
1321
|
}
|
package/src/index.mjs
CHANGED
|
@@ -146,6 +146,7 @@ export {
|
|
|
146
146
|
} from './workspace.mjs';
|
|
147
147
|
export {materializeInstalledSdkRuntime} from './installed-sdk-runtime.mjs';
|
|
148
148
|
export {startDevServer} from './dev-server.mjs';
|
|
149
|
+
export {resolvePwaRetirementResponse} from './pwa-retirement.mjs';
|
|
149
150
|
export {createDevelopmentCore} from './core/development.mjs';
|
|
150
151
|
export {startSourceExampleServer} from './source-server.mjs';
|
|
151
152
|
export {acceptWebSocket} from './websocket.mjs';
|
|
@@ -151,6 +151,7 @@ export async function copyCoreRuntime(root, files, signal, {
|
|
|
151
151
|
}
|
|
152
152
|
|
|
153
153
|
export function coreEntrySource(application, version, services, {localAI, runtimes = [], packagedWeb = false} = {}) {
|
|
154
|
+
const launchDefaults = application.native?.launchContext;
|
|
154
155
|
const imageSelected = localAI?.runtimes?.some(function selectedImage(requirement) {
|
|
155
156
|
return (typeof requirement === 'string' ? requirement : requirement.id) === 'stable-diffusion.cpp';
|
|
156
157
|
}) ?? false;
|
|
@@ -199,6 +200,7 @@ export function coreEntrySource(application, version, services, {localAI, runtim
|
|
|
199
200
|
"import {fileURLToPath} from 'node:url';",
|
|
200
201
|
"import path from 'node:path';",
|
|
201
202
|
"import {isSea} from 'node:sea';",
|
|
203
|
+
...(launchDefaults === undefined ? [] : ["import {mkdir} from 'node:fs/promises';"]),
|
|
202
204
|
"import {readCoreLaunchContext, startCoreHost, runSharedCoreHost, startSharedCoreBridge} from 'arcane-os/core/host';",
|
|
203
205
|
'',
|
|
204
206
|
...(localAI === undefined ? [] : [
|
|
@@ -241,9 +243,15 @@ export function coreEntrySource(application, version, services, {localAI, runtim
|
|
|
241
243
|
' return resolved;',
|
|
242
244
|
' }),'
|
|
243
245
|
]),
|
|
244
|
-
' ...await readCoreLaunchContext()'
|
|
246
|
+
launchDefaults === undefined ? ' ...await readCoreLaunchContext()'
|
|
247
|
+
: ` ...await readCoreLaunchContext({appId: ${JSON.stringify(application.id)}, defaults: JSON.parse(${JSON.stringify(JSON.stringify(launchDefaults))})})`,
|
|
245
248
|
'};',
|
|
246
249
|
'',
|
|
250
|
+
...(launchDefaults === undefined ? [] : [
|
|
251
|
+
'// The shared endpoint and diagnostic log require their selected state directory.',
|
|
252
|
+
'if (context.sharedHost !== undefined) await mkdir(context.stateRoot, {recursive: true});',
|
|
253
|
+
''
|
|
254
|
+
]),
|
|
247
255
|
...(packagedWeb ? ['let packagedWeb;', ''] : []),
|
|
248
256
|
'async function createServices(register) {',
|
|
249
257
|
` const [${imports.map(function binding(selection) { return selection.binding; }).join(', ')}] = await Promise.all([`,
|
|
@@ -197,6 +197,7 @@ export function createPortableNativeProvider({services, packagedWeb = false, web
|
|
|
197
197
|
webRoot: 'app',
|
|
198
198
|
start: release.manifest?.app?.start ?? './index.html',
|
|
199
199
|
...(appDescriptor.native?.window === undefined ? {} : {window: {...appDescriptor.native.window}}),
|
|
200
|
+
...(appDescriptor.native?.launchContext === undefined ? {} : {launchContext: appDescriptor.native.launchContext}),
|
|
200
201
|
core: {
|
|
201
202
|
entry: 'runtime/arcane-core.mjs', transport: 'stdio', protocol: 'arcane/1', services: selectedServices,
|
|
202
203
|
...(localAI === undefined ? {} : {localAIRuntimes: bundled.runtimes})
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import {lstat, readFile} from 'node:fs/promises';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import contentTypes from 'node-http-server/mime-types';
|
|
4
|
+
import {APP_DESCRIPTOR_NAME, projectPackageManifest} from './app-descriptor.mjs';
|
|
5
|
+
import {APP_CONFIG_NAME, validateAppConfig} from './packager/core.mjs';
|
|
6
|
+
import {inspectWorkspaceProfile} from './workspace.mjs';
|
|
7
|
+
import {createPwaRetirementWorkerScript} from './pwa-worker.mjs';
|
|
8
|
+
|
|
9
|
+
export async function resolvePwaRetirementResponse({workspaceRoot = process.cwd(), appId} = {}) {
|
|
10
|
+
const profile = await inspectWorkspaceProfile(workspaceRoot);
|
|
11
|
+
if (profile.config.appsRoot !== '.') return null;
|
|
12
|
+
|
|
13
|
+
let manifestPath = path.join(profile.workspaceRoot, APP_DESCRIPTOR_NAME);
|
|
14
|
+
let source;
|
|
15
|
+
let authored = true;
|
|
16
|
+
try {
|
|
17
|
+
source = await readFile(manifestPath, 'utf8');
|
|
18
|
+
} catch (error) {
|
|
19
|
+
if (error?.code !== 'ENOENT') throw error;
|
|
20
|
+
authored = false;
|
|
21
|
+
manifestPath = path.join(profile.workspaceRoot, APP_CONFIG_NAME);
|
|
22
|
+
source = await readFile(manifestPath, 'utf8');
|
|
23
|
+
}
|
|
24
|
+
const value = JSON.parse(source);
|
|
25
|
+
const manifest = authored ? projectPackageManifest(value) : value;
|
|
26
|
+
validateAppConfig(
|
|
27
|
+
manifest, appId ?? manifest.id, profile.config, manifestPath
|
|
28
|
+
);
|
|
29
|
+
// Normalization defaults an omitted enabled field to false. Retirement
|
|
30
|
+
// instead consumes the author's explicit choice from the original record.
|
|
31
|
+
const pwa = authored ? value.package.pwa : value.pwa;
|
|
32
|
+
return createPwaRetirementResponse({workspaceRoot: profile.workspaceRoot, pwa});
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// Source serving already owns a current descriptor snapshot. Reuse that snapshot
|
|
36
|
+
// without introducing another descriptor read or changing its refresh lifetime.
|
|
37
|
+
export async function createPwaRetirementResponse({workspaceRoot, pwa}) {
|
|
38
|
+
if (pwa?.enabled !== false) return null;
|
|
39
|
+
try {
|
|
40
|
+
await lstat(path.join(workspaceRoot, 'arcane-sw.js'));
|
|
41
|
+
return null;
|
|
42
|
+
} catch (error) {
|
|
43
|
+
if (error?.code !== 'ENOENT') throw error;
|
|
44
|
+
}
|
|
45
|
+
return {
|
|
46
|
+
statusCode: 200,
|
|
47
|
+
headers: {
|
|
48
|
+
'Content-Type': contentTypes.js,
|
|
49
|
+
'Cache-Control': 'no-cache'
|
|
50
|
+
},
|
|
51
|
+
body: createPwaRetirementWorkerScript()
|
|
52
|
+
};
|
|
53
|
+
}
|