arcane-os 0.59.0 → 0.60.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 +32 -0
- package/README.md +1 -1
- package/browser-runtime/ai/core-model-assets.mjs +152 -0
- package/browser-runtime/core/client.mjs +9 -1
- package/browser-runtime/core/contracts.mjs +5 -0
- package/docs/reference/README.md +3 -0
- package/docs/reference/core/arcane-ai-contracts.md +31 -2
- package/docs/reference/core/arcane-api.md +13 -2
- package/docs/reference/core/reference/arcane-api/ai-and-ollama.md +28 -16
- package/docs/reference/core/reference/arcane-api/namespaces.md +14 -0
- package/docs/reference/core-client.md +74 -0
- package/docs/reference/core-native-packaging.md +66 -2
- package/docs/reference/inventory/package-api.json +2 -2
- package/docs/reference/inventory/runtime-modules.json +2 -2
- package/docs/reference/local-image-editing.md +79 -0
- package/docs/reference/model-assets.md +144 -0
- package/docs/reference/runtime-modules.md +37 -5
- package/docs/reference/sdk-api.md +53 -8
- package/package.json +3 -3
- package/runtime/arcane/modules/AI.js +28 -12
- package/runtime/arcane/modules/ThemeBootstrap.js +217 -2
- package/src/core/hosts/windows/ArcaneHost.cs +211 -0
- package/src/core/hosts/windows/ArcaneLauncher.cs +5 -0
- package/src/core/services/model-assets.mjs +267 -0
- package/src/local-ai/image-editing.mjs +159 -0
- package/src/native/windows-icon.mjs +582 -0
- package/src/native/windows-provider.mjs +60 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,37 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.60.0
|
|
4
|
+
|
|
5
|
+
- Add `Arcane.window.setTheme(presentation, {signal})` for the current native
|
|
6
|
+
window. Optional sRGB RGBA background/text colors, null resets and omitted
|
|
7
|
+
fields retain their explicit semantics; results distinguish accepted and
|
|
8
|
+
unsupported fields. This does not change system-wide appearance.
|
|
9
|
+
- Forward application-computed theme colors without blocking rendering.
|
|
10
|
+
Observe existing theme and page lifecycles, cancel superseded requests, and
|
|
11
|
+
preserve ordinary browser behavior and native window controls.
|
|
12
|
+
- Apply app-owned Windows icons to the window/taskbar and copied executable.
|
|
13
|
+
Portable Node assembly handles PNG/ICO resources without an application
|
|
14
|
+
compiler, preserving source images and other executable resources. Other
|
|
15
|
+
formats retain assembly with an explicit unsupported-branding diagnostic.
|
|
16
|
+
- Windows caption/text colors use the host's supported per-window DWM
|
|
17
|
+
attributes. Using the new Windows behavior requires the matching host asset;
|
|
18
|
+
platform acceptance is reported rather than inferred from a request.
|
|
19
|
+
- Include completed internal PNG-decoding and stored-model working-file
|
|
20
|
+
preparation primitives. Image-service registration and engine integration
|
|
21
|
+
remain separate work; this release does not claim app-ready image generation.
|
|
22
|
+
|
|
23
|
+
## 0.59.1
|
|
24
|
+
|
|
25
|
+
- Forward the existing AI synthesis signal into native Core synthesis and
|
|
26
|
+
preserve the established AI cancellation error contract.
|
|
27
|
+
- Carry `AI.fetchSTT(audio, signal)` cancellation through
|
|
28
|
+
`Arcane.speech.transcribe(request, {signal})` into the existing Core request
|
|
29
|
+
lifetime. Preserve complete requests and results, one-argument facade calls,
|
|
30
|
+
unrelated errors and the 180-second timeout. Actual inference interruption
|
|
31
|
+
and resource release remain with the selected speech service.
|
|
32
|
+
- No speech engine, model, runtime dependency, default-provider switch or data
|
|
33
|
+
migration is introduced.
|
|
34
|
+
|
|
3
35
|
## 0.59.0
|
|
4
36
|
|
|
5
37
|
- Reconcile marked generated PWA references when a root application's PWA is
|
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.60.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;
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
import Is from '../dependencies/strong-type/index.js';
|
|
2
|
+
import {getInstalledCoreClient} from '../core/client.mjs';
|
|
3
|
+
|
|
4
|
+
const is = new Is(false);
|
|
5
|
+
|
|
6
|
+
/** Projects complete caller-owned assets through Core without changing their store. */
|
|
7
|
+
export async function prepareCoreModelAssets({
|
|
8
|
+
client = getInstalledCoreClient(), workingDirectory, members, signal, onProgress
|
|
9
|
+
} = {}) {
|
|
10
|
+
if (!is.function(client?.invoke) || !is.function(client?.uuid)) {
|
|
11
|
+
const error = new Error('Native model assets require an available Core connection.');
|
|
12
|
+
error.code = 'ARCANE_MODEL_ASSETS_CORE_UNAVAILABLE';
|
|
13
|
+
throw error;
|
|
14
|
+
}
|
|
15
|
+
if (!is.string(workingDirectory) || workingDirectory === '') {
|
|
16
|
+
throw new TypeError('Model asset workingDirectory must be a nonempty string.');
|
|
17
|
+
}
|
|
18
|
+
if (!is.array(members) || members.length === 0) {
|
|
19
|
+
throw new TypeError('Model assets require at least one complete file.');
|
|
20
|
+
}
|
|
21
|
+
for (const member of members) {
|
|
22
|
+
if (!is.string(member?.path) || member.path === '' || !is.function(member?.file?.stream)) {
|
|
23
|
+
throw new TypeError('Each model asset requires a path and a complete Blob or File.');
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
if (onProgress !== undefined && !is.function(onProgress)) {
|
|
27
|
+
throw new TypeError('Model asset onProgress must be a function.');
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const controller = new AbortController();
|
|
31
|
+
const operationSignal = signal ? AbortSignal.any([signal, controller.signal]) : controller.signal;
|
|
32
|
+
operationSignal.throwIfAborted();
|
|
33
|
+
const id = client.uuid();
|
|
34
|
+
const cleanupErrors = [];
|
|
35
|
+
let completed = 0;
|
|
36
|
+
let openingStarted = false;
|
|
37
|
+
let failed = false;
|
|
38
|
+
let failure;
|
|
39
|
+
let releaseTask;
|
|
40
|
+
|
|
41
|
+
function retainFailure(error) {
|
|
42
|
+
if (failed) return;
|
|
43
|
+
failed = true;
|
|
44
|
+
failure = error;
|
|
45
|
+
controller.abort(error);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function progress(phase, memberIndex) {
|
|
49
|
+
operationSignal.throwIfAborted();
|
|
50
|
+
onProgress?.({
|
|
51
|
+
phase, completed, total: members.length, unit: 'files',
|
|
52
|
+
...(memberIndex === undefined ? {} : {memberIndex, path: members[memberIndex].path})
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function release() {
|
|
57
|
+
releaseTask ??= Promise.resolve().then(function releaseNativeProjection() {
|
|
58
|
+
return client.invoke('modelAssets.release', {id}, {timeoutMs: 0});
|
|
59
|
+
});
|
|
60
|
+
return releaseTask;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
async function transferMember(member, memberIndex) {
|
|
64
|
+
let reader;
|
|
65
|
+
let cancellation;
|
|
66
|
+
|
|
67
|
+
function cancelReader() {
|
|
68
|
+
if (!reader || cancellation) return;
|
|
69
|
+
cancellation = Promise.resolve().then(function cancelMemberStream() {
|
|
70
|
+
return reader.cancel(operationSignal.reason);
|
|
71
|
+
}).catch(function retainReaderCleanupFailure(error) {
|
|
72
|
+
cleanupErrors.push(error);
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
try {
|
|
77
|
+
operationSignal.throwIfAborted();
|
|
78
|
+
reader = member.file.stream().getReader();
|
|
79
|
+
operationSignal.addEventListener('abort', cancelReader, {once: true});
|
|
80
|
+
if (operationSignal.aborted) cancelReader();
|
|
81
|
+
progress('write', memberIndex);
|
|
82
|
+
while (true) {
|
|
83
|
+
operationSignal.throwIfAborted();
|
|
84
|
+
const {done, value} = await reader.read();
|
|
85
|
+
operationSignal.throwIfAborted();
|
|
86
|
+
if (done) break;
|
|
87
|
+
// Base64 belongs only to the existing JSON transport boundary.
|
|
88
|
+
const contentBase64 = encodeChunk(value);
|
|
89
|
+
await client.invoke('modelAssets.write', {id, memberIndex, contentBase64}, {
|
|
90
|
+
signal: operationSignal, timeoutMs: 0
|
|
91
|
+
});
|
|
92
|
+
progress('write', memberIndex);
|
|
93
|
+
}
|
|
94
|
+
completed += 1;
|
|
95
|
+
progress('write', memberIndex);
|
|
96
|
+
} catch (error) {
|
|
97
|
+
retainFailure(error);
|
|
98
|
+
throw error;
|
|
99
|
+
} finally {
|
|
100
|
+
operationSignal.removeEventListener('abort', cancelReader);
|
|
101
|
+
if (operationSignal.aborted) cancelReader();
|
|
102
|
+
if (cancellation) await cancellation;
|
|
103
|
+
try { reader?.releaseLock(); } catch (error) {
|
|
104
|
+
if (failed) cleanupErrors.push(error);
|
|
105
|
+
else retainFailure(error);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
try {
|
|
111
|
+
progress('open');
|
|
112
|
+
openingStarted = true;
|
|
113
|
+
await client.invoke('modelAssets.open', {
|
|
114
|
+
id, workingDirectory,
|
|
115
|
+
members: members.map(function describeMember(member) { return {path: member.path}; })
|
|
116
|
+
}, {signal: operationSignal, timeoutMs: 0});
|
|
117
|
+
operationSignal.throwIfAborted();
|
|
118
|
+
const results = await Promise.allSettled(members.map(transferMember));
|
|
119
|
+
for (const result of results) {
|
|
120
|
+
if (result.status === 'rejected') retainFailure(result.reason);
|
|
121
|
+
}
|
|
122
|
+
if (failed) throw failure;
|
|
123
|
+
if (cleanupErrors.length) {
|
|
124
|
+
throw new AggregateError(cleanupErrors.splice(0), 'Model asset streams could not be released.');
|
|
125
|
+
}
|
|
126
|
+
progress('complete');
|
|
127
|
+
const projection = await client.invoke('modelAssets.complete', {id}, {
|
|
128
|
+
signal: operationSignal, timeoutMs: 0
|
|
129
|
+
});
|
|
130
|
+
progress('ready');
|
|
131
|
+
operationSignal.throwIfAborted();
|
|
132
|
+
// The caller releases ownership after native consumers retain this projection.
|
|
133
|
+
return {id, directory: projection.directory, members: projection.members, release};
|
|
134
|
+
} catch (error) {
|
|
135
|
+
retainFailure(error);
|
|
136
|
+
if (openingStarted) {
|
|
137
|
+
try { await release(); } catch (cleanupError) { cleanupErrors.push(cleanupError); }
|
|
138
|
+
}
|
|
139
|
+
if (cleanupErrors.length) {
|
|
140
|
+
throw new AggregateError([failure, ...cleanupErrors],
|
|
141
|
+
'Model asset preparation and cleanup failed.', {cause: failure});
|
|
142
|
+
}
|
|
143
|
+
throw failure;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
function encodeChunk(content) {
|
|
148
|
+
if (is.function(content.toBase64)) return content.toBase64();
|
|
149
|
+
let binary = '';
|
|
150
|
+
for (const value of content) binary += String.fromCharCode(value);
|
|
151
|
+
return btoa(binary);
|
|
152
|
+
}
|
|
@@ -569,7 +569,7 @@ export function createCoreFacade(client){
|
|
|
569
569
|
mail:{send:request=>invoke('mail.send',request??{},{timeoutMs:450000})},
|
|
570
570
|
speech:{status:()=>invoke('speech.status',{},{timeoutMs:10000}),
|
|
571
571
|
synthesize:(request,options)=>invoke('speech.synthesize',request??{},{timeoutMs:180000,signal:options?.signal}),
|
|
572
|
-
transcribe:request=>invoke('speech.transcribe',request??{},{timeoutMs:180000})},
|
|
572
|
+
transcribe:(request,options)=>invoke('speech.transcribe',request??{},{timeoutMs:180000,signal:options?.signal})},
|
|
573
573
|
localAI:{status:()=>invoke('localai.status',{},{timeoutMs:15000}),
|
|
574
574
|
ensurePlatform:()=>invoke('localai.platform.ensure',{},long),recover:request=>invoke('localai.services.recover',request??{},long),
|
|
575
575
|
setParallelRequests:request=>invoke('localai.parallel.requests.set',request??{},long),
|
|
@@ -616,6 +616,14 @@ export function createCoreFacade(client){
|
|
|
616
616
|
storage:{list:()=>invoke('storage.list'),get:key=>invoke('storage.get',{key}),set:(key,value)=>invoke('storage.set',{key,value}),delete:key=>invoke('storage.delete',{key})},
|
|
617
617
|
preferences:{list:()=>invoke('preferences.list'),get:key=>invoke('preferences.get',{key}),set:(key,value)=>invoke('preferences.set',{key,value}),
|
|
618
618
|
setMany:entries=>invoke('preferences.setMany',{entries}),delete:key=>invoke('preferences.delete',{key})},
|
|
619
|
+
window: {
|
|
620
|
+
setTheme(presentation, options) {
|
|
621
|
+
return invoke(
|
|
622
|
+
'window.setTheme', presentation,
|
|
623
|
+
{signal: options?.signal}
|
|
624
|
+
);
|
|
625
|
+
}
|
|
626
|
+
},
|
|
619
627
|
appearance:{current:()=>invoke('appearance.current'),apply:appearance=>invoke('appearance.apply',appearance??{})},session:{logout:()=>invoke('session.logout')},
|
|
620
628
|
provisioning:{plan:usernames=>invoke('provisioning.plan',{usernames:Array.isArray(usernames)?usernames:[usernames].filter(Boolean)})},
|
|
621
629
|
diagnostics:{recentErrors:()=>invoke('diagnostics.recent'),get:diagnosticId=>invoke('diagnostics.get',{diagnosticId})},
|
|
@@ -32,6 +32,11 @@ export const CORE_METHOD_CONTRACTS={
|
|
|
32
32
|
'preferences.set':{input:'{key,value}',output:'{key,value}'},
|
|
33
33
|
'preferences.setMany':{input:'{entries}',output:'preference-batch-result'},
|
|
34
34
|
'preferences.delete':{input:'{key}',output:'{key,deleted}'},
|
|
35
|
+
'window.setTheme': {
|
|
36
|
+
input: '{backgroundColor?,textColor?}: null reset or {red,green,blue,alpha}',
|
|
37
|
+
output: '{platform,supported,applied,unsupported}',
|
|
38
|
+
meaning: 'Current host window only. Omitted colors stay unchanged; applied records platform-accepted values, not measured pixels or an atomic operation.'
|
|
39
|
+
},
|
|
35
40
|
'terminal.start':{input:'terminal-start-v1',output:'terminal-session-v1'},
|
|
36
41
|
'terminal.list':{input:'empty-object-v1',output:'terminal-list-v1'},
|
|
37
42
|
'terminal.write':{input:'terminal-write-v1',output:'terminal-write-result-v1'},
|
package/docs/reference/README.md
CHANGED
|
@@ -42,6 +42,7 @@ alone does not make bare module names resolve in a browser.
|
|
|
42
42
|
| Inspect complete AI and speech calls using the shared developer-mode preference | [Shared logger](sdk-api.md#arcanelogging) and [speech developer diagnostics](ai/browser-speech.md#developer-diagnostics) |
|
|
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
|
+
| Disable source PWA generation or explicitly retire confirmed generated output | [Source PWA retirement](pwa.md#disable-source-pwa-generation-and-retire-generated-output) |
|
|
45
46
|
| 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) |
|
|
46
47
|
| Connect an application through the shared WebSocket transport | [Shared WebSocket clients](websocket-client.md) |
|
|
47
48
|
| Choose browser, native, cloud, or cross-host behavior | [Availability and normalization](availability-and-normalization.md) |
|
|
@@ -51,12 +52,14 @@ alone does not make bare module names resolve in a browser.
|
|
|
51
52
|
| 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) |
|
|
52
53
|
| Call `globalThis.Arcane` | [Arcane Core API](core/arcane-api.md) |
|
|
53
54
|
| Connect a browser to one Core transport | [Core browser client](core-client.md) |
|
|
55
|
+
| Cancel one Core speech synthesis request without changing its payload | [Speech request lifetime](core-client.md#events-and-request-lifetime) and [Core facade](sdk-api.md#createcorefacade) |
|
|
54
56
|
| Compose native Core dispatch, service lifecycle, and stdio hosting | [Native Core runtime](core-runtime.md) |
|
|
55
57
|
| Run application-selected llama.cpp, Ollama or ONNX through Core | [Local AI through Core](local-ai.md) |
|
|
56
58
|
| Retain a Nemotron model and diarize caller-fed audio streams | [Native speaker diarization](diarization.md) |
|
|
57
59
|
| Persist application-selected preferences through Core | [Core preferences service](core-preferences.md) |
|
|
58
60
|
| Read complete selected Git text without changing a checkout | [Git text snapshots](git-text-snapshot.md) |
|
|
59
61
|
| Build an installed-SDK portable Core payload | [Portable Core packaging](core-native-packaging.md) |
|
|
62
|
+
| Pass host-selected state defaults and unchanged explicit launch values to services | [Launch-time locations](core-native-packaging.md#launch-time-locations) |
|
|
60
63
|
| Build a Windows executable without an OS checkout | [Windows executable packaging](core-native-packaging.md#windows-executable) |
|
|
61
64
|
| Compose a Mac application with an architecture-matched host | [macOS application composition](core-native-packaging.md#macos-application-composition) |
|
|
62
65
|
| Subscribe to native events | [Arcane event reference](core/arcane-events.md) |
|
|
@@ -542,7 +542,7 @@ validate the speech health response.
|
|
|
542
542
|
|
|
543
543
|
### Speech synthesis request
|
|
544
544
|
|
|
545
|
-
`Arcane.speech.synthesize(request)` accepts the portable
|
|
545
|
+
`Arcane.speech.synthesize(request, options?)` accepts the portable
|
|
546
546
|
`SpeechSynthesisRequest` fields below. Supply the documented primitive types and
|
|
547
547
|
lowercase values; do not rely on host-specific coercion or empty-string
|
|
548
548
|
defaulting.
|
|
@@ -555,6 +555,22 @@ defaulting.
|
|
|
555
555
|
| `responseFormat` | `"opus" \| "wav"` | No | Default `"opus"` | Audio format. |
|
|
556
556
|
| `speed` | `number` | No | Default `1`; 0.5-2 inclusive | Speaking speed. |
|
|
557
557
|
|
|
558
|
+
### Speech synthesis controls
|
|
559
|
+
|
|
560
|
+
The optional second argument is `SpeechSynthesisControls`:
|
|
561
|
+
|
|
562
|
+
| Field | Type | Required | Default | Description |
|
|
563
|
+
|---|---|---:|---|---|
|
|
564
|
+
| `signal` | `AbortSignal` | No | No caller signal | Cancels this request through Core, separately from the complete synthesis payload. The registered service owns actual engine interruption. |
|
|
565
|
+
|
|
566
|
+
Existing one-argument calls retain their behavior and the 180-second timeout.
|
|
567
|
+
A pre-aborted signal dispatches no request. An in-flight abort sends
|
|
568
|
+
`request.cancel`, rejects with `ARCANE_REQUEST_ABORTED` and suppresses late
|
|
569
|
+
responses while unrelated requests continue. Other controls properties are
|
|
570
|
+
unused and do not enter the payload. `AI.fetchTTS(payload, signal)` forwards
|
|
571
|
+
the signal into this native call and reports cancellation as `AbortError` with
|
|
572
|
+
`ARCANE_AI_REQUEST_ABORTED`.
|
|
573
|
+
|
|
558
574
|
### Speech synthesis result
|
|
559
575
|
|
|
560
576
|
| Field | Type | Required | Constraints / default | Description |
|
|
@@ -564,7 +580,7 @@ defaulting.
|
|
|
564
580
|
|
|
565
581
|
### Speech transcription request
|
|
566
582
|
|
|
567
|
-
`Arcane.speech.transcribe(request)` accepts an exact
|
|
583
|
+
`Arcane.speech.transcribe(request, options?)` accepts an exact
|
|
568
584
|
`SpeechTranscriptionRequest`.
|
|
569
585
|
|
|
570
586
|
| Field | Type | Required | Constraints / default | Description |
|
|
@@ -573,6 +589,19 @@ defaulting.
|
|
|
573
589
|
| `mimeType` | `string` | No | Default `"audio/webm"`; base type must be `audio/webm` | Media type; codec parameters may follow. |
|
|
574
590
|
| `model` | `string` | No | Default `"whisper-small"`; `whisper(?:[._-][a-z0-9]+)*` | Whisper identifier. The fixed service rejects unsupported variants. |
|
|
575
591
|
|
|
592
|
+
### Speech transcription controls
|
|
593
|
+
|
|
594
|
+
The optional second argument is `SpeechTranscriptionControls`:
|
|
595
|
+
|
|
596
|
+
| Field | Type | Required | Default | Description |
|
|
597
|
+
|---|---|---:|---|---|
|
|
598
|
+
| `signal` | `AbortSignal` | No | No caller signal | Cancels this request through Core. The facade forwards it separately from the complete audio request; the registered service owns actual engine interruption. |
|
|
599
|
+
|
|
600
|
+
Existing one-argument calls retain their behavior. The facade's request timeout
|
|
601
|
+
remains 180 seconds. A pre-aborted signal dispatches no request, and an
|
|
602
|
+
in-flight abort cancels the request and suppresses late responses. Other
|
|
603
|
+
properties of this controls record are unused; they do not enter the payload.
|
|
604
|
+
|
|
576
605
|
### Speech transcription result
|
|
577
606
|
|
|
578
607
|
| Field | Type | Required | Constraints / default | Description |
|
|
@@ -48,6 +48,7 @@ This inventory covers every enumerable, non-method member exposed directly or th
|
|
|
48
48
|
| `Arcane.filesystem` | Namespace | Native filesystem selection operations. |
|
|
49
49
|
| `Arcane.storage` | Namespace | App-scoped native key/value storage. |
|
|
50
50
|
| `Arcane.preferences` | Namespace | App-scoped preference reads and atomic writes. |
|
|
51
|
+
| `Arcane.window` | Namespace | SDK host window presentation for the calling document; adapter availability is independent of the OS-user appearance service. |
|
|
51
52
|
| `Arcane.appearance` | Namespace | Native appearance state and application. |
|
|
52
53
|
| `Arcane.session` | Namespace | Current Arcane session lifecycle. |
|
|
53
54
|
| `Arcane.provisioning` | Namespace | Provisioning-plan operations. |
|
|
@@ -104,8 +105,8 @@ directly.
|
|
|
104
105
|
| `Arcane.localAI.recover(request)` | `request`: [`LocalAIRecoveryRequest`](arcane-ai-contracts.md#local-ai-recovery-request) | [`Promise<LocalAIRecoveryResult>`](arcane-ai-contracts.md#local-ai-recovery-result) | Requests one privileged, exclusive, start-existing-service-only recovery of selected Arcane-managed local services. Requires `ai.inference` and explicit application admission. |
|
|
105
106
|
| `Arcane.localAI.setParallelRequests(request)` | `request`: [`LocalAIParallelRequestsRequest`](arcane-ai-contracts.md#local-ai-parallel-request-configuration) | [`Promise<LocalAIParallelRequestsResult>`](arcane-ai-contracts.md#local-ai-parallel-request-configuration) | Solves one app-owned verified model/context's maximum directly from native model and memory/resource evidence without a product ceiling, clamps a positive request to that result (`0` requests the maximum), and changes the machine-wide managed Ollama setting only when needed. A tentative change uses one fresh stopped-service snapshot before the final value is written, then Core ensures, loads, and confirms the exact model. A restart unloads resident models. Automatic mutation currently requires the dedicated `ai.runtime.manage` capability on Microsoft NT desktop Core and operating-system authorization; Linux returns administrator-managed systemd guidance, and Android user-managed loopback sessions do not invoke it. |
|
|
106
107
|
| `Arcane.speech.status()` | None | [`Promise<SpeechStatus>`](arcane-ai-contracts.md#speech-status) | Reads bounded, independent Kokoro and Whisper readiness through Core or an admitted Android native host. Requires `ai.inference`. |
|
|
107
|
-
| `Arcane.speech.synthesize(request)` | `request`: [`SpeechSynthesisRequest`](arcane-ai-contracts.md#speech-synthesis-request) | [`Promise<SpeechSynthesisResult>`](arcane-ai-contracts.md#speech-synthesis-result) |
|
|
108
|
-
| `Arcane.speech.transcribe(request)` | `request`: [`SpeechTranscriptionRequest`](arcane-ai-contracts.md#speech-transcription-request) | [`Promise<SpeechTranscriptionResult>`](arcane-ai-contracts.md#speech-transcription-result) | Sends
|
|
108
|
+
| `Arcane.speech.synthesize(request, options?)` | `request`: [`SpeechSynthesisRequest`](arcane-ai-contracts.md#speech-synthesis-request); `options?`: [`SpeechSynthesisControls`](arcane-ai-contracts.md#speech-synthesis-controls) | [`Promise<SpeechSynthesisResult>`](arcane-ai-contracts.md#speech-synthesis-result) | Sends the complete request to the selected native synthesis service; optional cancellation reaches Core's request lifetime. The registered host service owns audio generation and actual engine interruption. |
|
|
109
|
+
| `Arcane.speech.transcribe(request, options?)` | `request`: [`SpeechTranscriptionRequest`](arcane-ai-contracts.md#speech-transcription-request); `options?`: [`SpeechTranscriptionControls`](arcane-ai-contracts.md#speech-transcription-controls) | [`Promise<SpeechTranscriptionResult>`](arcane-ai-contracts.md#speech-transcription-result) | Sends the complete request to the selected native transcription service; optional cancellation reaches Core's request lifetime. The registered host service owns media decoding and actual engine interruption. |
|
|
109
110
|
| `Arcane.ollama.version()` | None | [`Promise<OllamaVersionResponse>`](arcane-ai-contracts.md#ollama-provider-responses) | Gets the managed Ollama version for Settings, Terminal, and Shell diagnostics. |
|
|
110
111
|
| `Arcane.ollama.models()` | None | [`Promise<OllamaModelsResponse>`](arcane-ai-contracts.md#ollama-provider-responses) | Lists the raw provider inventory envelope; it is not the application admission catalog. |
|
|
111
112
|
| `Arcane.ollama.list()` | None | [`Promise<OllamaModelsResponse>`](arcane-ai-contracts.md#ollama-provider-responses) | Alias of the raw `models()` inventory. |
|
|
@@ -438,6 +439,16 @@ The canonical authority registry in `machine_bundles/arcane-os-machine-bundle/sr
|
|
|
438
439
|
|
|
439
440
|
## Filesystem, storage, preferences, and appearance
|
|
440
441
|
|
|
442
|
+
The SDK's additive `Arcane.window.setTheme(presentation, {signal}?)` controls
|
|
443
|
+
the current host window's `backgroundColor` and `textColor`, with per-field
|
|
444
|
+
accepted/unsupported results. See the [window contract](../core-client.md#current-window-theme)
|
|
445
|
+
for numeric RGBA, reset, cancellation, navigation and platform availability.
|
|
446
|
+
It does not call the OS-user `appearance.apply` service described below.
|
|
447
|
+
|
|
448
|
+
| SDK window method | Parameters | Return | Description |
|
|
449
|
+
|---|---|---|---|
|
|
450
|
+
| `Arcane.window.setTheme(presentation, options?)` | Optional `backgroundColor`/`textColor`: `{red,green,blue,alpha}` or `null`; `options.signal?` | `Promise<{platform,supported,applied,unsupported}>` | Applies supported colors only to the caller's native window, preserving omitted fields. Uses ordinary request cancellation and reports actual per-field acceptance. See the linked contract for adapter availability. |
|
|
451
|
+
|
|
441
452
|
| Method | Parameters | Return | Description |
|
|
442
453
|
|---|---|---|---|
|
|
443
454
|
| `Arcane.filesystem.selectDirectory(options?)` | `options?`: `{title?, initialPath?}`; title up to 200 plain-text characters; initial path must be an existing absolute directory | `Promise<{cancelled:boolean, path:string\|null}>` | Opens the native directory picker after a user action. It returns one canonical existing directory or explicit cancellation; it does not enumerate or grant access to directory contents. Requires `filesystem.directory.select`. |
|
|
@@ -646,7 +646,7 @@ console.info({
|
|
|
646
646
|
|
|
647
647
|
### Overview
|
|
648
648
|
|
|
649
|
-
`Arcane.speech.synthesize(request)` converts bounded text to local speech. Use
|
|
649
|
+
`Arcane.speech.synthesize(request, options?)` converts bounded text to local speech. Use
|
|
650
650
|
it only after checking synthesis readiness, and create an audio URL from the
|
|
651
651
|
returned bytes without logging the base64 payload.
|
|
652
652
|
|
|
@@ -683,27 +683,36 @@ owned active operation before retrying.
|
|
|
683
683
|
### Streaming, cancellation, and events
|
|
684
684
|
|
|
685
685
|
Synthesis returns one complete audio result and emits no method event. The
|
|
686
|
-
wrapper timeout
|
|
687
|
-
|
|
686
|
+
wrapper timeout remains 180 seconds. Optional `{signal}` controls the Core
|
|
687
|
+
request lifetime separately from the complete synthesis payload. Pre-abort
|
|
688
|
+
sends no request; in-flight abort sends `request.cancel`, rejects with
|
|
689
|
+
`ARCANE_REQUEST_ABORTED` and suppresses late responses. Unrelated requests
|
|
690
|
+
continue. The registered native service owns actual synthesis interruption and
|
|
691
|
+
resource release; the facade supplies no engine. `AI.fetchTTS(payload, signal)`
|
|
692
|
+
forwards its signal through this call and normalizes cancellation to
|
|
693
|
+
`AbortError` with `ARCANE_AI_REQUEST_ABORTED`.
|
|
688
694
|
|
|
689
695
|
### Example
|
|
690
696
|
|
|
691
697
|
```javascript
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
698
|
+
async function synthesizeMoonEncore(input, signal) {
|
|
699
|
+
return globalThis.Arcane.speech.synthesize(
|
|
700
|
+
{
|
|
701
|
+
input,
|
|
702
|
+
voice: 'af_heart',
|
|
703
|
+
responseFormat: 'opus',
|
|
704
|
+
speed: 1
|
|
705
|
+
},
|
|
706
|
+
{signal}
|
|
707
|
+
);
|
|
708
|
+
}
|
|
700
709
|
```
|
|
701
710
|
|
|
702
711
|
## Arcane.speech.transcribe()
|
|
703
712
|
|
|
704
713
|
### Overview
|
|
705
714
|
|
|
706
|
-
`Arcane.speech.transcribe(request)` sends bounded caller-supplied audio bytes,
|
|
715
|
+
`Arcane.speech.transcribe(request, options?)` sends bounded caller-supplied audio bytes,
|
|
707
716
|
labeled as WebM, to the fixed local transcription service. Use it after local
|
|
708
717
|
capture permission and recording have succeeded; the method itself does not
|
|
709
718
|
capture audio or grant microphone permission.
|
|
@@ -739,18 +748,21 @@ work after `ANDROID_SPEECH_QUEUE_FULL`.
|
|
|
739
748
|
### Streaming, cancellation, and events
|
|
740
749
|
|
|
741
750
|
Transcription resolves one complete text result and emits no method event. The
|
|
742
|
-
wrapper timeout
|
|
743
|
-
|
|
751
|
+
wrapper timeout remains 180 seconds. Optional `{signal}` controls the Core
|
|
752
|
+
request lifetime separately from the complete audio payload. Pre-abort sends
|
|
753
|
+
no request; in-flight abort cancels that request and suppresses late responses.
|
|
754
|
+
The registered native service owns actual inference interruption and resource
|
|
755
|
+
release. `AI.fetchSTT(audio, signal)` forwards its signal through this call.
|
|
744
756
|
|
|
745
757
|
### Example
|
|
746
758
|
|
|
747
759
|
```javascript
|
|
748
|
-
async function transcribeRecordedWebM(audioBase64) {
|
|
760
|
+
async function transcribeRecordedWebM(audioBase64, signal) {
|
|
749
761
|
const result = await globalThis.Arcane.speech.transcribe({
|
|
750
762
|
audioBase64: audioBase64,
|
|
751
763
|
mimeType: 'audio/webm',
|
|
752
764
|
model: 'whisper-small'
|
|
753
|
-
});
|
|
765
|
+
}, {signal});
|
|
754
766
|
return result.text;
|
|
755
767
|
}
|
|
756
768
|
```
|
|
@@ -1038,6 +1038,20 @@ const canReadPreferences = access.methods.includes('preferences.list');
|
|
|
1038
1038
|
console.log('Preference inventory admitted:', canReadPreferences);
|
|
1039
1039
|
```
|
|
1040
1040
|
|
|
1041
|
+
## Arcane.window
|
|
1042
|
+
|
|
1043
|
+
The SDK client exposes `Arcane.window.setTheme(presentation, {signal}?)` for
|
|
1044
|
+
the window hosting the calling document. The optional `backgroundColor` and
|
|
1045
|
+
`textColor` accept numeric RGBA records or `null` for platform-default reset.
|
|
1046
|
+
The host reports each applied or unsupported field independently. Rendering
|
|
1047
|
+
and browser theme changes continue without waiting for this request.
|
|
1048
|
+
|
|
1049
|
+
Shared ThemeBootstrap forwards the app's computed semantic colors through
|
|
1050
|
+
this method when a native host is present. It does not change OS-user
|
|
1051
|
+
appearance. See the [current-window contract](../../../core-client.md#current-window-theme)
|
|
1052
|
+
for complete usage, accepted channel values, cancellation and current adapter
|
|
1053
|
+
availability. A facade method alone does not establish host support.
|
|
1054
|
+
|
|
1041
1055
|
## Arcane.appearance
|
|
1042
1056
|
|
|
1043
1057
|
### Overview
|
|
@@ -83,6 +83,80 @@ the 180,000 ms timeout and pass the complete request unchanged. A pre-aborted
|
|
|
83
83
|
signal sends no synthesis request; an in-flight abort sends `request.cancel`
|
|
84
84
|
and suppresses late responses. Actual synthesis interruption depends on the
|
|
85
85
|
registered speech service and its engine; the facade supplies no speech engine.
|
|
86
|
+
`AI.fetchTTS(payload, signal)` carries its caller-owned signal into this native
|
|
87
|
+
call and normalizes cancellation to `AbortError` with
|
|
88
|
+
`ARCANE_AI_REQUEST_ABORTED`, preserving the original error as its cause.
|
|
89
|
+
|
|
90
|
+
`Arcane.speech.transcribe(request, {signal})` forwards cancellation through the
|
|
91
|
+
same request lifetime and retains its 180,000 ms timeout and complete request.
|
|
92
|
+
`AI.fetchSTT(audio, signal)` carries that signal into the native call. A
|
|
93
|
+
pre-aborted signal sends no transcription request; an in-flight abort sends
|
|
94
|
+
`request.cancel` and suppresses late responses. The registered transcription
|
|
95
|
+
service owns interruption and release of its actual engine operation.
|
|
96
|
+
|
|
97
|
+
## Current-window theme
|
|
98
|
+
|
|
99
|
+
`Arcane.window.setTheme(presentation, {signal}?)` changes only the window
|
|
100
|
+
hosting the calling document. It uses the existing `window.setTheme` request
|
|
101
|
+
and response correlation; it neither calls `appearance.apply` nor changes the
|
|
102
|
+
operating-system user's appearance. The complete presentation object reaches
|
|
103
|
+
the host unchanged.
|
|
104
|
+
|
|
105
|
+
Each optional `backgroundColor` and `textColor` is either `null` to restore the
|
|
106
|
+
platform default or `{red, green, blue, alpha}`. All four channels are numbers:
|
|
107
|
+
finite sRGB red/green/blue from 0 through 255 and alpha from 0 through 1.
|
|
108
|
+
Omitting a field leaves its current value unchanged. There is no scheme field.
|
|
109
|
+
|
|
110
|
+
```js
|
|
111
|
+
const host=globalThis.Arcane;
|
|
112
|
+
if(host?.runtime?.current().native&&host.window?.setTheme){
|
|
113
|
+
host.window.setTheme(
|
|
114
|
+
{
|
|
115
|
+
backgroundColor:{red:29,green:22,blue:19,alpha:1},
|
|
116
|
+
textColor:{red:248,green:239,blue:229,alpha:1}
|
|
117
|
+
}
|
|
118
|
+
).then(
|
|
119
|
+
function reportWindowColors(result){
|
|
120
|
+
console.log('Moon observatory window colors:',result);
|
|
121
|
+
}
|
|
122
|
+
).catch(
|
|
123
|
+
function reportWindowFailure(error){
|
|
124
|
+
console.error('Window color update failed.',error);
|
|
125
|
+
}
|
|
126
|
+
);
|
|
127
|
+
}
|
|
128
|
+
// Page rendering continues independently of the host request.
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
The result is `{platform, supported, applied, unsupported}`. `applied` contains
|
|
132
|
+
only requested fields the platform accepted in this call, including `null`
|
|
133
|
+
for an accepted default reset. `unsupported` lists requested fields the adapter
|
|
134
|
+
cannot apply. `supported` is true when at least one field was applied. These
|
|
135
|
+
are accepted settings, not measured pixels, and the operation is not atomic.
|
|
136
|
+
Malformed arguments reject with `INVALID_ARGUMENT`; native API failures reject
|
|
137
|
+
with `ARCANE_WINDOW_THEME_FAILED`, preserving the native error and
|
|
138
|
+
`details.applied`/`details.unsupported` for earlier results in this call.
|
|
139
|
+
|
|
140
|
+
The Windows host maps background/text to DWM caption/text attributes supported
|
|
141
|
+
on Windows 11 build 22000 and later, retaining normal system window controls.
|
|
142
|
+
It rounds RGB channels to the nearest integer, with midpoint values rounded
|
|
143
|
+
up, and returns those actual accepted values. Translucent colors remain
|
|
144
|
+
unsupported without changing the existing field. Older Windows reports those
|
|
145
|
+
fields unsupported. This contract makes no promise about caption-button colors.
|
|
146
|
+
The current macOS and Linux hosts have no implementation of this method;
|
|
147
|
+
their ordinary unavailable-method response remains observable. A new facade
|
|
148
|
+
alone does not add the operation to an older native executable. Use the
|
|
149
|
+
matching published host artifact for the selected SDK version.
|
|
150
|
+
|
|
151
|
+
Cancellation uses the normal request lifetime and suppresses late responses;
|
|
152
|
+
it does not roll back a setting the host already accepted. Window settings
|
|
153
|
+
survive document navigation until a subsequent document changes them or the
|
|
154
|
+
window closes. Responses belong to their originating document.
|
|
155
|
+
|
|
156
|
+
The shared [ThemeBootstrap](runtime-modules.md#themebootstrapjs) automatically
|
|
157
|
+
forwards the app's computed `--background` and `--text-color` in native hosts.
|
|
158
|
+
Applications using that owner need no duplicate theme listener. Ordinary
|
|
159
|
+
browsers keep their CSS presentation without making a native request.
|
|
86
160
|
|
|
87
161
|
## Events and request lifetime
|
|
88
162
|
|
|
@@ -303,6 +303,70 @@ observes complete diagnostics and exit. Cancelling that operation closes its
|
|
|
303
303
|
input, allowing the window and accepted Core service work to drain without a
|
|
304
304
|
forced process termination.
|
|
305
305
|
|
|
306
|
+
### Application icon and window colors
|
|
307
|
+
|
|
308
|
+
`appDescriptor.native.icon` selects the application's existing app-relative
|
|
309
|
+
image. Windows assembly converts PNG images, including a 512-pixel source,
|
|
310
|
+
into a derived `runtime/arcane-app.ico` with 16, 32, 48 and 256-pixel images.
|
|
311
|
+
Aspect ratio and transparency are preserved. An ICO selection keeps its
|
|
312
|
+
authored image set. The original application asset remains unchanged.
|
|
313
|
+
|
|
314
|
+
The provider embeds the selected images in the copied `Arcane.exe` and records
|
|
315
|
+
`manifest.host.icon` and `manifest.host.executableIcon:true`. It replaces the
|
|
316
|
+
executable's main icon group while preserving other groups, languages, resources
|
|
317
|
+
and file content. Conversion uses Node standard capabilities on every assembly
|
|
318
|
+
platform; it adds no application compiler, image library or Windows-only build
|
|
319
|
+
step. The selected host cache and Core executable remain unchanged.
|
|
320
|
+
|
|
321
|
+
JPEG/JPG, WebP and other existing descriptor formats retain ordinary assembly.
|
|
322
|
+
They currently report `native.icon.unsupported` through the builder's `onEvent`
|
|
323
|
+
callback, as does a selected executable whose PE layout cannot accommodate the
|
|
324
|
+
derived icon. The event identifies the app, icon, target, code and complete
|
|
325
|
+
message; `manifest.host.executableIcon:false` reports that the executable keeps
|
|
326
|
+
its prior icon. `host.icon` then points to the original selected image. This is
|
|
327
|
+
an explicit unsupported branding result, not a claim that a generic icon was
|
|
328
|
+
replaced. Unreadable files and malformed supported image data report their
|
|
329
|
+
ordinary filesystem or image-parser error.
|
|
330
|
+
|
|
331
|
+
The matching Windows launcher reads `host.icon` and loads it independently of
|
|
332
|
+
Core and page startup. ICO and standard Windows image codecs supply the window
|
|
333
|
+
and taskbar icon; JPEG/JPG can therefore work at runtime even while executable
|
|
334
|
+
embedding is unsupported. WebP is outside the host's standard image codecs;
|
|
335
|
+
runtime support is not promised. A runtime icon error preserves ordinary startup
|
|
336
|
+
and reaches complete developer diagnostics as `ARCANE_WINDOW_ICON_UNAVAILABLE`.
|
|
337
|
+
Hosts built before this option was introduced do not read the new icon field.
|
|
338
|
+
|
|
339
|
+
`Arcane.window.setTheme(presentation,{signal}?)` changes only the requesting
|
|
340
|
+
native window's chrome. The shared ThemeBootstrap supplies computed application
|
|
341
|
+
background and text colors without delaying application CSS or rendering.
|
|
342
|
+
Direct callers may supply `backgroundColor` and `textColor`, each either
|
|
343
|
+
`{red,green,blue,alpha}` or `null` to restore the platform default. RGB channels
|
|
344
|
+
are finite numbers from 0 through 255 and alpha is from 0 through 1. Omitted
|
|
345
|
+
fields remain unchanged. The complete presentation follows the existing Core
|
|
346
|
+
request transport; the window host handles `window.setTheme` before forwarding
|
|
347
|
+
other methods to Core. No system appearance setting changes.
|
|
348
|
+
|
|
349
|
+
Windows 11 build 22000 and later support titlebar background and text colors
|
|
350
|
+
through DWM. The normal titlebar and window controls remain in place. DWM accepts
|
|
351
|
+
opaque colors, so alpha values other than exactly 1 report that field as
|
|
352
|
+
unsupported. RGB values round to the nearest integer, with midpoint values
|
|
353
|
+
rounded upward. Older Windows versions report the requested fields unsupported.
|
|
354
|
+
|
|
355
|
+
The result is `{platform:'windows',supported,applied,unsupported}`. `applied`
|
|
356
|
+
contains only fields accepted during that call, including rounded channels or
|
|
357
|
+
`null` for a successful default reset. `supported` is true when at least one
|
|
358
|
+
field applied; `unsupported` lists requested fields the platform could not
|
|
359
|
+
apply. These results report platform acceptance rather than measured pixels.
|
|
360
|
+
An invalid argument uses `INVALID_ARGUMENT`; a platform API failure uses
|
|
361
|
+
`ARCANE_WINDOW_THEME_FAILED`, preserving complete native details and any
|
|
362
|
+
earlier changes in `error.details.applied` and `error.details.unsupported`.
|
|
363
|
+
The call is not atomic across fields. Existing request cancellation and document
|
|
364
|
+
lifetime semantics remain in effect; an already accepted native color change
|
|
365
|
+
is not rolled back by later cancellation. See [the Core client](core-client.md)
|
|
366
|
+
for the facade and browser behavior.
|
|
367
|
+
|
|
368
|
+
### Launch configuration
|
|
369
|
+
|
|
306
370
|
For explicit user-selected workspace/state locations, launch:
|
|
307
371
|
|
|
308
372
|
```sh
|
|
@@ -331,8 +395,8 @@ exit. It does not kill the child on an ordinary close.
|
|
|
331
395
|
`ArcaneHost.cs` composes that process with a WebView2 window through
|
|
332
396
|
`ArcaneHost.Run(options,onDiagnostic,onError)` or `ArcaneHostForm`. Options
|
|
333
397
|
select the app root/start path, stable virtual HTTPS origin, profile directory,
|
|
334
|
-
title, canonical classic-client source and Core process
|
|
335
|
-
`Ready`, `Completion` and `CloseAsync()`. Page navigation, transport connection,
|
|
398
|
+
title, optional `IconPath`, canonical classic-client source and Core process
|
|
399
|
+
inputs. The form exposes `Ready`, `Completion` and `CloseAsync()`. Page navigation, transport connection,
|
|
336
400
|
Core readiness and service readiness are separate states. Accepted work drains
|
|
337
401
|
before the window closes; complete engineering errors go to the supplied
|
|
338
402
|
diagnostic/error callbacks. Callbacks must not synchronously wait for the
|