arcane-os 0.56.0 → 0.58.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.
Files changed (75) hide show
  1. package/CHANGELOG.md +43 -0
  2. package/README.md +6 -2
  3. package/browser-runtime/ai/core-local.mjs +306 -0
  4. package/browser-runtime/ai/core-onnx.mjs +96 -0
  5. package/browser-runtime/ai/onnx-tensors.mjs +85 -0
  6. package/browser-runtime/core/client.mjs +151 -16
  7. package/docs/architecture.md +5 -2
  8. package/docs/platform-targets.md +30 -13
  9. package/docs/reference/README.md +38 -3
  10. package/docs/reference/availability-and-normalization.md +9 -6
  11. package/docs/reference/cli.md +29 -9
  12. package/docs/reference/core-client.md +80 -0
  13. package/docs/reference/core-native-packaging.md +304 -12
  14. package/docs/reference/core-preferences.md +49 -0
  15. package/docs/reference/diarization.md +206 -0
  16. package/docs/reference/git-text-snapshot.md +104 -0
  17. package/docs/reference/inventory/package-api.json +330 -22
  18. package/docs/reference/local-ai.md +331 -0
  19. package/docs/reference/protocols.md +97 -20
  20. package/docs/reference/pwa.md +5 -2
  21. package/docs/reference/runtime-modules.md +6 -5
  22. package/docs/reference/sdk-api.md +611 -63
  23. package/package.json +15 -3
  24. package/runtime/arcane/modules/LocalAIReadiness.js +3 -1
  25. package/schemas/arcane-app.schema.json +55 -1
  26. package/schemas/arcane-package.schema.json +44 -0
  27. package/schemas/native-build-plan.schema.json +27 -1
  28. package/schemas/target-adapter.schema.json +2 -0
  29. package/src/app-descriptor.mjs +18 -2
  30. package/src/cli/main.mjs +20 -4
  31. package/src/constants.mjs +2 -0
  32. package/src/core/host.mjs +23 -0
  33. package/src/core/hosts/macos/ArcaneCoreProcess.h +85 -0
  34. package/src/core/hosts/macos/ArcaneCoreProcess.m +357 -0
  35. package/src/core/hosts/macos/ArcaneHost.h +86 -0
  36. package/src/core/hosts/macos/ArcaneHost.m +455 -0
  37. package/src/core/hosts/macos/ArcaneLauncher.m +432 -0
  38. package/src/core/hosts/windows/ArcaneCoreProcess.cs +3 -3
  39. package/src/core/hosts/windows/ArcaneHost.cs +695 -0
  40. package/src/core/hosts/windows/ArcaneHost.manifest +22 -0
  41. package/src/core/hosts/windows/ArcaneLauncher.cs +239 -0
  42. package/src/core/services/local-ai.mjs +621 -0
  43. package/src/core/services/packaged-web.mjs +89 -0
  44. package/src/core/services/preferences.mjs +201 -0
  45. package/src/core-classic-source.mjs +2 -2
  46. package/src/dev-server.mjs +9 -5
  47. package/src/diarization/build.mjs +35 -0
  48. package/src/diarization/index.mjs +432 -0
  49. package/src/diarization/native/CMakeLists.txt +9 -0
  50. package/src/diarization/native/main.cpp +269 -0
  51. package/src/diarization/service.mjs +129 -0
  52. package/src/git-text-snapshot.mjs +265 -0
  53. package/src/import-map.mjs +2 -0
  54. package/src/index.mjs +1 -0
  55. package/src/local-ai/archive.mjs +52 -0
  56. package/src/local-ai/config.mjs +62 -0
  57. package/src/local-ai/dev.mjs +390 -0
  58. package/src/local-ai/discover.mjs +71 -0
  59. package/src/local-ai/http.mjs +106 -0
  60. package/src/local-ai/install.mjs +459 -0
  61. package/src/local-ai/onnx-worker.mjs +85 -0
  62. package/src/local-ai/onnx.mjs +368 -0
  63. package/src/local-ai/server.mjs +262 -0
  64. package/src/native/macos-provider.mjs +291 -0
  65. package/src/native/portable-layout.mjs +37 -4
  66. package/src/native/portable-provider.mjs +44 -8
  67. package/src/native/windows-provider.mjs +262 -0
  68. package/src/native-plan.mjs +9 -1
  69. package/src/native-provider-loader.mjs +10 -2
  70. package/src/packager/core.mjs +6 -2
  71. package/src/process.mjs +184 -20
  72. package/src/scaffold.mjs +1 -1
  73. package/src/targets/index.mjs +11 -1
  74. package/src/templates/workspace-template.mjs +6 -5
  75. package/src/toolchain.mjs +33 -5
@@ -124,6 +124,86 @@ developer diagnostics, rather than ordinary
124
124
  product status surfaces. The optional `onError(error)` callback observes
125
125
  transport/event-owner failures outside individual request promises.
126
126
 
127
+ ## Optional WebKit document lifetime
128
+
129
+ `webKitDocumentLifecycle:true` on `createCoreClient`, `installCoreClient` or
130
+ `createCoreClassicSource` selects document lifetime handling for the native
131
+ WebKit `arcane` handler with replies. It defaults to `false`; existing WebKit
132
+ plain-JSON and alternate acknowledgement transports, WebView2, Android,
133
+ development HTTP and supplied transports keep their existing behavior. The
134
+ selected host must implement this contract and the existing `runtime.replay`
135
+ control. Install the classic source at document start in the page's world.
136
+
137
+ The client owns a fresh activation for the current JavaScript realm and each
138
+ restoration from the browser's page cache. WithReply receives these transport
139
+ records; the Core JSON string remains unchanged in its separate `json` field:
140
+
141
+ | Record | Meaning |
142
+ | --- | --- |
143
+ | `{type:'activate', activation}` | Attach this activation before sending its Core frames. |
144
+ | `{type:'frame', activation, json}` | Send the complete serialized Core request or control. |
145
+ | `{type:'retire', activation, json}` | Retire this activation; `json` is the existing `requests.cancelAll` control. |
146
+
147
+ Reply with `{accepted:true}` or `{accepted:false,error}` on the initiating
148
+ WithReply promise. Frame and retirement acceptance follows the actual complete
149
+ process write. Queue admission alone is insufficient. Activation acceptance
150
+ follows the host's current-realm probe and any required prior-owner cancellation.
151
+ Only this activation's outgoing frames wait for attachment; page rendering,
152
+ facade installation and independent Core startup remain concurrent.
153
+ An aborted, timed-out or otherwise cancelled request awaiting attachment is
154
+ never submitted afterward; its existing cancellation control remains observable.
155
+
156
+ The host calls `__arcaneWebKitDocumentCurrent(activation)` in the current main
157
+ document's page world to probe a candidate. A token or native frame descriptor
158
+ alone does not identify the active document. Serialize candidate probes and
159
+ Core ingress ownership changes: finish each candidate's probe before processing
160
+ the next announcement, and leave the current owner unchanged when a probe is
161
+ false. Announcement arrival order cannot establish document order. A true
162
+ probe establishes the realm at that execution instant; every later delivery
163
+ still checks its target at execution time.
164
+
165
+ Before accepting a replacement activation, write the existing
166
+ `requests.cancelAll` control for the prior ingress owner. Do not wait for a
167
+ retirement announcement that a destroyed document may never send. Write a
168
+ retirement record's JSON only while that activation still owns ingress;
169
+ reject a late retirement without cancelling the replacement's requests.
170
+ Record request-ID-to-origin-activation correlation before submitting each
171
+ request to Core. Responses retain that origin. Existing generic service events
172
+ remain broadcasts to the current activation; no request association is added.
173
+ Complete native diagnostics retain frames received while no document is active;
174
+ restoration uses current-state replay rather than historical event delivery.
175
+
176
+ Deliver through `__arcaneReceive(json, activation)` and
177
+ `__arcaneTransportFailed(error, activation)`, passing payloads as native
178
+ JavaScript call arguments. Both return `false` for a retired or different
179
+ activation. Receive also returns `false` for an unrecognized frame or a
180
+ response with no pending request; `true` means the client accepted delivery,
181
+ which may still await its shared event owner. Failure returns `true` when it
182
+ ends that active installation. Preserve actual evaluation failures through the
183
+ native diagnostic owner; a `false` result is not a JavaScript exception.
184
+ `client.acceptsNativeDelivery(activation)` exposes the same synchronous lifetime
185
+ predicate. It accepts any activation while a client using an ordinary transport
186
+ is live. Direct owner calls to `client.failTransport(error)` retain their
187
+ existing terminal-failure semantics.
188
+
189
+ Page hide synchronously retires the activation even with no pending requests.
190
+ It clears completed ready state and rejects every pending renderer promise with
191
+ the existing `ARCANE_REQUEST_ABORTED`/`AbortError`. This ends the page's wait;
192
+ the runtime's cancellation skips service-lifetime work, so an accepted save
193
+ continues and participates in graceful drain. Its eventual response stays with
194
+ the retired activation and cannot settle a restored page's request. The client
195
+ does not claim that cancellation rolled back persistence.
196
+
197
+ Persisted page restoration creates a fresh activation and sends exactly one
198
+ `runtime.replay` after native attachment. Initial attachment sends that replay
199
+ only when `replayRuntimeState:true`. Application subscriptions survive page
200
+ hide and restoration, including registrations awaiting the shared event owner
201
+ and ready listeners awaiting their replay microtask. Queued deliveries from a
202
+ retired activation are no longer applicable to those subscriptions. Replay
203
+ supplies current Core/service state, not prior responses or request promises.
204
+ Closing the client retires once, removes lifecycle listeners and restores its
205
+ owned callbacks. It does not close the process or control application shutdown.
206
+
127
207
  ## Classic native-host injection
128
208
 
129
209
  ```js
@@ -1,22 +1,32 @@
1
1
  # Portable Core packaging
2
2
 
3
3
  `arcane-os/native/portable-provider` owns the portable native payload in the SDK
4
- package. It does not load Arcane OS source, choose models, download a runtime,
5
- or compile an executable. Windows, Linux and macOS hosts may compose this
4
+ package. It does not load Arcane OS source, choose models or compile an
5
+ executable. An application's explicit `native.localAI` selection can include
6
+ its selected upstream runtimes through the SDK local-AI installer. Without that
7
+ selection, assembly does no local-AI runtime preparation. Windows, Linux and macOS hosts may compose this
6
8
  Node-based payload; Android needs its separate native host adaptation. The
7
- current CLI's portable platform selections remain Windows and Linux. A portable
9
+ current CLI's portable platform selections include Windows, Linux and macOS. A portable
8
10
  artifact is not a Windows executable, Linux package or Android application.
9
11
 
10
12
  `loadArcaneNativeProvider({target:'portable'})` selects the installed SDK provider
11
13
  without an `arcaneRoot`. Supplying an explicit `arcaneRoot` retains the existing
12
- checkout-provider route. Other native targets remain with their actual platform
14
+ checkout-provider route. The SDK also owns the `windows-x64` executable provider
15
+ described below. Other native targets remain with their actual platform
13
16
  providers; the loader does not substitute this payload for an unavailable host.
14
17
 
15
18
  ## Provider and selected input
16
19
 
17
20
  The default export and `arcaneNativeBuilderProvider` are the same provider.
18
- `createPortableNativeProvider({services:[]})` creates one with application-owned
19
- service configuration. All implement `arcane-native-builder/1`:
21
+ `createPortableNativeProvider({services,packagedWeb=false,webKitDocumentLifecycle=false})` creates one with application-owned
22
+ service configuration. Without that override, the provider uses the selected
23
+ app descriptor's `native.services`, or an empty selection when omitted. An
24
+ explicit `services:[]` overrides the descriptor with no app services.
25
+ The Mac provider selects `packagedWeb:true` to compose the SDK's packaged-web
26
+ Core service and `webKitDocumentLifecycle:true` for the canonical classic-client
27
+ activation/replay contract. Both default to false, preserving ordinary portable
28
+ and Windows composition.
29
+ All implement `arcane-native-builder/1`:
20
30
 
21
31
  - `describe()` identifies the portable target and `executable:false`.
22
32
  - `doctor({targetRequest, signal, onEvent})` reports missing SDK Core source files.
@@ -42,6 +52,45 @@ receipts, hashes, content limits or security policy. The package-relative source
42
52
  root comes from the provider module's own location, not `toolchainRoot` or an
43
53
  Arcane OS machine bundle.
44
54
 
55
+ ## Native-only application resources
56
+
57
+ Select application service modules and their complete local source closure in
58
+ the authored schema-2 descriptor's `package.nativeResources`, separately from
59
+ its browser `include`/`exclude` selection:
60
+
61
+ ```json
62
+ {
63
+ "package": {
64
+ "nativeResources": {
65
+ "include": ["native", "src", "package.json"],
66
+ "exclude": ["native/drafts"]
67
+ }
68
+ }
69
+ }
70
+ ```
71
+
72
+ This is a descriptor excerpt for the app-owned service example below. The
73
+ projected schema-1 field is top-level `nativeResources`. Paths are literal and
74
+ app-relative; directories include descendants. Omission or an empty include
75
+ keeps previous selection and output behavior. The app supplies every required
76
+ source file explicitly; packaging does not discover or execute services.
77
+
78
+ Paired `buildApplication()` passes the actual native target to the selected app
79
+ and every explicitly bundled app. With a nonempty native include list, their
80
+ input releases use `dist/.native/<target>/<app-id>` instead of the saved browser
81
+ output, preserving existing browser/PWA artifacts. Each app's own selection is
82
+ applied; complete releases and actual roots are forwarded to the provider.
83
+ The provider then copies them unchanged into its artifact layout described below.
84
+
85
+ At the low-level `packageApp()` boundary, `target` defaults to `'browser'` and
86
+ is independent of `moduleFormat`. Nonbrowser selection disables generated PWA
87
+ content. Native-only files bypass browser document/module/asset transformations;
88
+ a file also in the ordinary browser selection retains that existing processing.
89
+ An explicit low-level `outputDirectory` still wins. Inspect the emitted input
90
+ with `verifyApp({workspaceRoot,appId,outputDirectory:release.output})`; neither
91
+ that inventory verification nor packaging launches the service or platform host.
92
+ See [the complete selection contract](protocols.md#native-only-application-resources).
93
+
45
94
  ## Output and host contract
46
95
 
47
96
  Each build creates a fresh `arcane-portable-*` directory beneath `outputRoot`.
@@ -85,7 +134,9 @@ host must:
85
134
  1. Mount `app/` at the root of its local application URL origin, preserving the
86
135
  selected start path and all navigable documents.
87
136
  2. Inject `runtime/arcane-api.js` at document creation. It is produced by the
88
- canonical `createCoreClassicSource()` generator, not a copied OS client.
137
+ canonical `createCoreClassicSource()` generator with `replayRuntimeState:true`.
138
+ Once its transport listener is attached, each document requests the current
139
+ Core and service state without starting services again.
89
140
  3. Start `runtime/arcane-core.mjs` with the host's supported Node runtime and
90
141
  connect the existing framed stdin/stdout transport.
91
142
  4. Close input and await Core's drain before terminating the child process.
@@ -100,7 +151,21 @@ is not the native serving contract. No host is launched during packaging.
100
151
 
101
152
  ## Explicit application services
102
153
 
103
- Pass native service modules already included in the selected app release:
154
+ For the ordinary SDK build command, select native service modules in the
155
+ application's authored descriptor. Include their source closure through
156
+ `package.nativeResources` as described above:
157
+
158
+ ```json
159
+ {
160
+ "native": {
161
+ "services": [
162
+ {"module":"native/moon-ledger.mjs", "options":{"ledgerName":"Cheese debts"}}
163
+ ]
164
+ }
165
+ }
166
+ ```
167
+
168
+ Direct SDK compositions can instead pass that selection to the provider:
104
169
 
105
170
  ```js
106
171
  import {createPortableNativeProvider} from 'arcane-os/native/portable-provider';
@@ -111,7 +176,8 @@ const nativeBuilder = createPortableNativeProvider({
111
176
  ```
112
177
 
113
178
  Each module default-exports a synchronous factory returning the existing Core
114
- service definition. Its options must be JSON-serializable data. Module imports
179
+ service definition. Its first argument is the complete authored options record;
180
+ its second is the shared launch context described below. Options must be JSON-serializable data. Module imports
115
181
  and factories run only when the generated Core entry is launched, never during
116
182
  packaging. Put asynchronous initialization in `start()`, not a top-level await
117
183
  or asynchronous factory, so unrelated services and dispatcher readiness remain
@@ -142,8 +208,234 @@ observable through `onError` and `closed`; no process is killed or forcibly exit
142
208
  See [the Core runtime contract](core-runtime.md) for request and service lifetime
143
209
  details and [the client contract](core-client.md) for native bridge integration.
144
210
 
145
- This source increment supplies portable assembly and service composition.
146
- Desktop WebView executables, platform child-process ownership, Android dispatch
147
- and executable build toolchains require their own completed adapters. Published
211
+ ## Launch-time locations
212
+
213
+ The generated Core entry accepts `--arcane-launch-config <path>`. The public
214
+ `readCoreLaunchContext({argv=process.argv.slice(2)}={})` function in
215
+ `arcane-os/core/host` reads that explicitly selected JSON object. Without the
216
+ flag it returns `{}`. Missing files, missing flag values and malformed JSON
217
+ report their actual errors; the reader does not search for configuration,
218
+ rewrite it or change the process environment.
219
+
220
+ The generated entry supplies an artifact-derived `appRoot` and then applies the
221
+ complete explicit launch context. Every service factory receives that same
222
+ object as its second argument, preserving its authored first argument. A
223
+ launcher can supply the user's selected `workspaceRoot` and writable
224
+ `stateRoot` at launch instead of embedding this machine's paths in an app
225
+ package. The host owns those selections; the SDK does not choose a product's
226
+ workspace, namespace, saved preferences or models. JSON values and additional
227
+ application-owned fields remain unchanged.
228
+
229
+ ## Selected local-AI runtimes
230
+
231
+ When `appDescriptor.native.localAI` is present, portable assembly calls the
232
+ SDK's `bundleLocalAIRuntimes()` owner with its `runtimes` selection and the
233
+ requested target platform/architecture. Its reusable installation directory is
234
+ `<outputRoot>/local-ai-runtimes`; each new payload receives the complete selected
235
+ runtime trees beneath `runtime/local-ai/`. Runtime preparation and app copying
236
+ proceed independently and both settle before assembly can clean up an incomplete
237
+ output. No application service or model executes during packaging.
238
+
239
+ `manifest.core.localAIRuntimes` records the bundled runtime locations relative
240
+ to the artifact root. Only the explicitly configured entry imports
241
+ `createLocalAIService` from `arcane-os/core/local-ai`. It resolves those bundled
242
+ locations against the installed artifact and supplies them in the launch
243
+ context's `runtimes` array, alongside `appRoot`, before applying explicit launch
244
+ configuration. It passes the complete authored `native.localAI` record to the
245
+ factory. Applications without that selection have no generated local-AI import
246
+ or service. Availability of a particular engine belongs to the selected
247
+ local-AI installer and service; a generic ONNX runtime is not a speech model.
248
+
249
+ Selected `onnx` bundles its complete managed `onnxruntime-node` dependency tree.
250
+ Its `modulePath` resolves against the artifact root just as a server runtime's
251
+ `executable` does. Library/module records need no executable. The generated Core
252
+ service exposes retained worker sessions through `onnx.load/run/unload/status`;
253
+ model files remain caller-selected application resources. See
254
+ [native ONNX sessions](local-ai.md#native-onnx-sessions).
255
+
256
+ ## Windows executable
257
+
258
+ The ordinary application command is:
259
+
260
+ ```sh
261
+ npm exec -- arcane build --target windows-x64
262
+ ```
263
+
264
+ `loadArcaneNativeProvider({target:'windows-x64'})` selects the SDK's own
265
+ `arcane-os/native/windows-provider` without an OS checkout or `arcaneRoot`.
266
+ Its default export and `arcaneNativeBuilderProvider` are the same provider;
267
+ `createWindowsNativeProvider({services,hostDirectory})` permits explicit
268
+ same-project composition. Service selection follows the portable provider's
269
+ descriptor/override contract above.
270
+
271
+ The provider assembles the selected app with the precompiled host asset
272
+ `arcane-native-windows-x64.tar.gz` from the numeric GitHub release matching the
273
+ builder's installed SDK version. The asset contains the launcher, WebView2
274
+ support libraries, Node SEA Core executable and required license files. It does
275
+ not select the newest host independently of that package. Downloads occur only
276
+ during `build()`, use the caller's output directory, and are reused from
277
+ `<outputRoot>/.arcane-native-hosts/<sdk-version>/windows-x64`. The build machine
278
+ needs the standard `tar` command for extraction; application builds do not need
279
+ a C# compiler, system Node installation for the generated executable, or host
280
+ source scripts. The SDK build command itself still runs through the application's
281
+ normal Node/npm toolchain.
282
+
283
+ An explicit `hostDirectory` selects an already-built complete host tree instead
284
+ of downloading one. This is useful for SDK collaborators verifying the next
285
+ host output; downstream applications use the published package and matching
286
+ release asset. `doctor()` reports the selected prerequisites and missing files;
287
+ it does not download the host or probe the installed WebView runtime.
288
+
289
+ The assembled directory contains `Arcane.exe`, the unchanged `app/` payload,
290
+ Core runtime and semantic `arcane-native.json`. Launch `Arcane.exe` on Windows
291
+ x64 with the Microsoft Edge WebView2 Runtime installed. It opens the selected
292
+ app in WebView2 and owns its framed connection to the bundled Core process.
293
+ `verify()` observes the assembled files and manifest; it does not execute the
294
+ window or prove application behavior. `run()` launches the executable and
295
+ observes complete diagnostics and exit. Cancelling that operation closes its
296
+ input, allowing the window and accepted Core service work to drain without a
297
+ forced process termination.
298
+
299
+ For explicit user-selected workspace/state locations, launch:
300
+
301
+ ```sh
302
+ Arcane.exe --arcane-launch-config path/to/launch.json
303
+ ```
304
+
305
+ The complete JSON object reaches the service factories as their second
306
+ argument. A supplied `stateRoot` also owns the WebView2 profile and diagnostic
307
+ directory. Otherwise the launcher uses the current user's local application
308
+ data under `Arcane/<app-id>`. The launcher does not invent an application
309
+ workspace. Relative launch-file paths resolve from the invocation's working
310
+ directory. `--close-on-stdin-eof` is an explicit process-owner option used by the
311
+ SDK runner; an ordinary double-clicked window closes through its own UI.
312
+
313
+ ## Windows host source and selected output
314
+
315
+ The SDK's `src/core/hosts/windows/ArcaneCoreProcess.cs` owns an explicitly
316
+ selected executable, argument string and working directory. It reads complete
317
+ framed Core messages and diagnostics, acknowledges ordered asynchronous writes,
318
+ and closes stdin before awaiting accepted Core work, final output and process
319
+ exit. It does not kill the child on an ordinary close.
320
+
321
+ `ArcaneHost.cs` composes that process with a WebView2 window through
322
+ `ArcaneHost.Run(options,onDiagnostic,onError)` or `ArcaneHostForm`. Options
323
+ select the app root/start path, stable virtual HTTPS origin, profile directory,
324
+ title, canonical classic-client source and Core process inputs. The form exposes
325
+ `Ready`, `Completion` and `CloseAsync()`. Page navigation, transport connection,
326
+ Core readiness and service readiness are separate states. Accepted work drains
327
+ before the window closes; complete engineering errors go to the supplied
328
+ diagnostic/error callbacks. Callbacks must not synchronously wait for the
329
+ window's lifetime promises.
330
+
331
+ `ArcaneLauncher.cs` supplies the executable entry point for that composition.
332
+ `tools/build-core-windows-host.mjs` builds the selected SDK host output from the
333
+ approved WebView2 package, existing .NET Framework compiler and selected Node
334
+ SEA output. It retains complete compiler diagnostics and produces a fresh
335
+ directory rather than overwriting an earlier artifact. The host requires
336
+ .NET Framework 4.6.2 or later. This is SDK release tooling, not an application
337
+ build requirement. Source compilation, selected host execution and real
338
+ application behavior are separate evidence boundaries; an assembled app is not
339
+ an installer or proof of its domain behavior.
340
+
341
+ Linux, macOS, Android dispatch and each platform's output verification retain
342
+ their separate delivery boundaries. Published
148
343
  consumers must adopt the numeric package that includes this increment; a source
149
344
  checkout or a successful source push is not that public package authority.
345
+
346
+ ## macOS process source
347
+
348
+ `src/core/hosts/macos/ArcaneCoreProcess.h` and `.m` provide a Foundation-only
349
+ adapter for macOS 11 or later, compiled with Objective-C ARC and blocks. A
350
+ composing host explicitly supplies the executable URL, arguments, working
351
+ directory and delegate; startup is asynchronous. Delegate callbacks deliver
352
+ every raw stdout/stderr chunk and complete opaque framed JSON bodies, plus
353
+ launch, error, exit and final completion observations. Each stream and accepted
354
+ write retains its own order; UI dispatch belongs to the host. Callbacks must
355
+ return normally and must not wait for another callback or completion.
356
+
357
+ `sendJSONData:completion:error:` accepts an ordered write or reports a
358
+ synchronous rejection. Write completion means the frame reached stdin, not that
359
+ the RPC succeeded. `closeInput` rejects new sends, finishes accepted writes and
360
+ closes stdin, then keeps observing output and child exit without a forced
361
+ termination deadline. The delegate is retained through final completion.
362
+ The AppKit `ArcaneHost` and launcher compose that source as described below.
363
+ Compilation and execution on macOS remain separate evidence from the selected
364
+ Windows output.
365
+
366
+ ## macOS application composition
367
+
368
+ `arcane-os/native/macos-provider` exports
369
+ `createMacOSNativeProvider({services,hostDirectory}={})`, its named
370
+ `arcaneNativeBuilderProvider`, and the same provider as default. The six-method
371
+ contract accepts `macos-arm64` or `macos-x64`, `platform:'macos'`, `format:'app'`
372
+ and the existing unsigned local development selection. Ordinary CLI routing
373
+ uses the SDK package, not an Arcane OS checkout:
374
+
375
+ ```sh
376
+ npm exec -- arcane build --target macos-arm64
377
+ npm exec -- arcane run --target macos-arm64
378
+ ```
379
+
380
+ Select `macos-x64` for Intel. The descriptor must declare the selected target.
381
+ Source implementation does not establish that an architecture's host artifact
382
+ has been built, executed or published. Consumer assembly needs the exact numeric
383
+ SDK release asset `arcane-native-macos-<architecture>.tar.gz`; unavailable assets
384
+ produce their actual download error. SDK collaborators may select an existing
385
+ `hostDirectory` explicitly. There is no fallback to source checkout, another
386
+ version, another architecture or a browser-only artifact.
387
+
388
+ The complete unchanged portable payload lives at
389
+ `<app-id>.app/Contents/Resources/`. That directory remains `target.rootDir` and
390
+ contains `arcane-native.json`, `app/`, dependency releases, installed SDK files,
391
+ Core entry/client and `runtime/node`. The additive `bundleRoot` identifies the
392
+ `.app` directory. `Contents/MacOS/Arcane` and `Contents/Info.plist` belong to the
393
+ bundle rather than the web root; the Mac owner checks these separately from the
394
+ Resources file inventory. Application identity supplies the stable bundle ID
395
+ `org.arcane.<app-id>`. Executable permissions are preserved for the launcher and
396
+ selected Node runtime.
397
+
398
+ The launcher opens its window immediately and starts one Core child. The
399
+ generated Core entry starts the `packaged-web` service alongside application
400
+ services, without waiting for models. Only `core.web.ready`, emitted after the
401
+ actual listener is ready and the initial port is saved, supplies the application
402
+ URL to WebKit. It serves the complete `manifest.webRoot` at `/` and resolves the
403
+ complete `manifest.start`, including query and fragment, against that origin.
404
+ The classic client selects current-document activation and sticky state replay;
405
+ navigation does not restart the Core service or replay retired requests.
406
+ The host's main-thread readonly `isClosing` accessor exposes its existing close
407
+ intent; an unsolicited Core exit is therefore reported separately from an
408
+ ordinary user-requested drain, even when the process exits successfully.
409
+
410
+ `createPackagedWebService({artifactRoot}, context={})` is available from
411
+ `arcane-os/core/packaged-web`. It uses the existing
412
+ `startDevServer({mode:'packaged',http:true,host:'127.0.0.1',port,releaseRoot})`
413
+ owner and published `node-http-server`, not a second HTTP implementation.
414
+ `context.stateRoot` takes precedence over the launcher's separate
415
+ `--arcane-host-state-root` argument. The launcher defaults to
416
+ `~/Library/Application Support/Arcane/<app-id>`; an explicitly supplied launch
417
+ file remains unchanged and is forwarded to Core. The first successful listener
418
+ stores its selected port in `packaged-web-origin.json`. Later launches reuse it;
419
+ a port conflict or unreadable saved record is reported without changing origins
420
+ or discarding stored state.
421
+
422
+ The app's persistent default `WKWebsiteDataStore` uses its bundle identity.
423
+ `stateRoot` selects Core state and diagnostics, not a custom WebKit profile
424
+ directory. Browser API availability, OPFS and secure-context behavior remain
425
+ WebKit-owned and require actual Mac verification; the SDK bypasses no browser
426
+ policy. Startup failures use `core.service.state` for `name:'packaged-web'`;
427
+ unexpected listener closure or failure uses `core.web.failed` with the complete
428
+ serialized error. Native alerts show a concise outcome, with full diagnostics
429
+ outside the conversation. Core drains accepted work and the listener before
430
+ the native close completes. The optional runner stdin-EOF signal uses that same
431
+ path; ordinary double-click startup does not wait on stdin.
432
+
433
+ `tools/build-core-macos-host.mjs` is SDK host-release tooling, not an application
434
+ compiler requirement. It accepts an explicit Darwin Node executable and its
435
+ license, compiler path, `arm64` or `x64`, and output directory. It uses Objective-C
436
+ ARC/blocks and Apple's Foundation, AppKit and WebKit frameworks. The host source
437
+ targets macOS 11; the selected Node runtime may require a newer macOS version.
438
+ Its actual runtime/platform/architecture and the SDK Node engine requirement
439
+ must agree. Compiler/runtime execution and redistribution belong to the
440
+ separately authorized selected host output. This source increment runs none of
441
+ them and claims no Mac build or execution result.
@@ -0,0 +1,49 @@
1
+ # Core preferences service
2
+
3
+ `createPreferencesService({file})` from `arcane-os/core/preferences` supplies the
4
+ existing `Arcane.preferences` RPC methods to a native Core host. The default
5
+ export is the same factory. Construction performs no filesystem work. The
6
+ application selects its writable file and owns preference names, defaults and
7
+ schemas; this service does not invent them or alter operating-system settings.
8
+
9
+ ```js
10
+ import path from 'node:path';
11
+ import {createPreferencesService} from 'arcane-os/core/preferences';
12
+
13
+ export default function createMoonPreferences(options, context) {
14
+ return createPreferencesService({
15
+ file: path.join(context.stateRoot, 'moon-preferences.json')
16
+ });
17
+ }
18
+ ```
19
+
20
+ Select this application-owned factory through `native.services`, and include it
21
+ in `package.nativeResources`. Supply the application's selected `stateRoot`
22
+ through the explicit launch configuration. See [native packaging](core-native-packaging.md#launch-time-locations).
23
+
24
+ | Method | Input | Result |
25
+ | --- | --- | --- |
26
+ | `preferences.list` | `{}` | `{keys}` in sorted order |
27
+ | `preferences.get` | `{key}` | `{key,found,value}`; missing value is `null` |
28
+ | `preferences.set` | `{key,value}` | `{key,value}` after file replacement |
29
+ | `preferences.setMany` | `{entries}` | `{keys,count}` after file replacement |
30
+ | `preferences.delete` | `{key}` | `{key,deleted}` |
31
+
32
+ Storage remains `{schemaVersion:1,entries}`. Exact keys, complete JSON values and
33
+ additional existing document metadata are retained. A missing file starts with
34
+ empty entries; reading it does not create a file. Unreadable or malformed files
35
+ report their real errors and are not replaced with an empty record. Values that
36
+ JSON cannot represent completely fail before replacement. There is no migration,
37
+ truncation, namespace rewrite, default injection or frozen result.
38
+
39
+ Operations selecting the same resolved filename share one ordered queue within
40
+ the Core process. This is not a cross-process file lock. Writes flush their new
41
+ content and replace the selected file before acknowledgment. Filesystem failures
42
+ remain observable, including cleanup errors; this is not a claim of survival
43
+ across every hardware or operating-system failure.
44
+
45
+ `set`, `setMany` and `delete` use `lifetime:'service'`: renderer cancellation does
46
+ not discard accepted writes. `drain()` and `dispose()` stop acceptance and wait
47
+ for accepted operations; later calls reject with `CORE_CLOSING`. Read operations
48
+ observe their request cancellation. Use the existing Core host shutdown owner
49
+ so saved preferences finish before the native process exits.