arcane-os 0.63.0 → 0.65.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 +48 -0
- package/README.md +1 -1
- package/browser-runtime/core/client.mjs +69 -7
- package/docs/guides/native-whisper-openvino.md +334 -0
- package/docs/reference/README.md +6 -0
- package/docs/reference/arcane-ollama.md +1 -1
- package/docs/reference/codex-app-server.md +269 -0
- package/docs/reference/core-client.md +71 -4
- package/docs/reference/core-development.md +157 -0
- package/docs/reference/core-native-packaging.md +48 -6
- package/docs/reference/core-repositories.md +258 -0
- package/docs/reference/inventory/package-api.json +199 -3
- package/docs/reference/inventory/runtime-modules.json +3 -2
- package/docs/reference/local-ai.md +77 -4
- package/docs/reference/local-image-flux.md +1 -1
- package/docs/reference/local-image-generation.md +11 -0
- package/docs/reference/local-image-sdxl.md +139 -0
- package/docs/reference/local-whisper.md +219 -0
- package/docs/reference/mcp-stdio.md +263 -0
- package/docs/reference/native-speech.md +51 -2
- package/docs/reference/runtime-components.md +18 -1
- package/docs/reference/runtime-modules.md +26 -4
- package/docs/reference/sdk-api.md +412 -2
- package/package.json +9 -2
- package/runtime/arcane/components/speech.html +15 -3
- package/runtime/arcane/components/voice-transcription.html +19 -3
- package/runtime/arcane/modules/AI.js +406 -40
- package/runtime/arcane/modules/AIProviderRuntime.js +20 -3
- package/runtime/arcane/modules/AIRuntimeState.js +15 -2
- package/runtime/arcane/modules/ComponentContracts.js +3 -0
- package/runtime/arcane/modules/Ollama.js +1 -1
- package/runtime/arcane/modules/OllamaModelIdentifier.js +13 -0
- package/schemas/arcane-app.schema.json +60 -2
- package/schemas/arcane-package.schema.json +60 -2
- package/src/codex/app-server.mjs +845 -0
- package/src/core/development.mjs +457 -0
- package/src/core/repositories.mjs +375 -0
- package/src/core/services/local-ai.mjs +80 -10
- package/src/core/services/speech.mjs +1 -0
- package/src/dev-server.mjs +3 -2
- package/src/index.mjs +1 -0
- package/src/local-ai/config.mjs +7 -3
- package/src/local-ai/dev.mjs +85 -401
- package/src/local-ai/image-library.mjs +9 -0
- package/src/local-ai/image-models/sdxl.mjs +18 -0
- package/src/local-ai/install.mjs +26 -7
- package/src/local-ai/whisper/build.mjs +48 -0
- package/src/local-ai/whisper/config.mjs +89 -0
- package/src/local-ai/whisper/index.mjs +572 -0
- package/src/local-ai/whisper/install.mjs +332 -0
- package/src/local-ai/whisper/native/CMakeLists.txt +98 -0
- package/src/local-ai/whisper/native/NOTICE +18 -0
- package/src/local-ai/whisper/native/README.md +104 -0
- package/src/local-ai/whisper/native/main.cpp +520 -0
- package/src/local-ai/whisper/openvino-build.mjs +138 -0
- package/src/mcp/stdio-server.mjs +579 -0
- package/src/native/portable-layout.mjs +39 -7
- package/src/toolchain.mjs +19 -8
|
@@ -41,6 +41,17 @@ model's resource metadata; runtime installation does not independently download
|
|
|
41
41
|
or bundle model files. The shared model asset owner prepares selected browser
|
|
42
42
|
assets and supplies a Core working projection for native path-based loading.
|
|
43
43
|
|
|
44
|
+
`flux2-klein-4b` selects the three-resource distilled FLUX.2 Klein definition.
|
|
45
|
+
Its [family guide](local-image-flux.md) covers diffusion/VAE/Qwen preparation,
|
|
46
|
+
four-step defaults and the advertised `txt2img` operation. Keep each model's
|
|
47
|
+
supported operations distinct: selecting FLUX does not enable reference-image
|
|
48
|
+
editing or imply the same input path as SD1.4 img2img.
|
|
49
|
+
|
|
50
|
+
`sdxl-base-1.0` selects the complete SDXL Base checkpoint, including its text
|
|
51
|
+
encoders and original VAE. Its [family guide](local-image-sdxl.md) covers the
|
|
52
|
+
single model resource, 1024-by-1024 default canvas and native sampling defaults.
|
|
53
|
+
It advertises `txt2img`; selecting it does not enable an editing operation.
|
|
54
|
+
|
|
44
55
|
The first published distribution path selects CPU on Windows and Linux.
|
|
45
56
|
On macOS, `auto` selects the same upstream universal runtime's built-in Metal
|
|
46
57
|
backend, with CPU fallback; explicit `cpu` remains available. A caller-supplied
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
# SDXL Base 1.0 through Core
|
|
2
|
+
|
|
3
|
+
The `sdxl-base-1.0` model definition selects Stability AI's complete SDXL Base
|
|
4
|
+
1.0 checkpoint for text-to-image generation through the shared
|
|
5
|
+
[local image runtime](local-image-generation.md). Applications supply their
|
|
6
|
+
own complete prompts and own the resulting images and saved associations.
|
|
7
|
+
The model uses the shared retained native context, progress, cancellation
|
|
8
|
+
and complete PNG result contract.
|
|
9
|
+
|
|
10
|
+
## Model selection
|
|
11
|
+
|
|
12
|
+
Select this runtime record under `native.localAI` in `arcane-app.json`, or
|
|
13
|
+
the corresponding package projection's `localAI` record:
|
|
14
|
+
|
|
15
|
+
```json
|
|
16
|
+
{
|
|
17
|
+
"runtimes": [
|
|
18
|
+
{
|
|
19
|
+
"id": "stable-diffusion.cpp",
|
|
20
|
+
"version": "master-929-3f8527a",
|
|
21
|
+
"backend": "auto",
|
|
22
|
+
"models": ["sdxl-base-1.0"]
|
|
23
|
+
}
|
|
24
|
+
]
|
|
25
|
+
}
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
The built-in ID supplies the cohesive `SDXL_BASE_1_0_MODEL` definition from
|
|
29
|
+
`src/local-ai/image-models/sdxl.mjs`. The shared service exposes its resource
|
|
30
|
+
metadata through `image.status` and `images.inspect()`.
|
|
31
|
+
|
|
32
|
+
Backend discovery, accelerated-device selection and CPU fallback belong to
|
|
33
|
+
the shared runtime. This model definition leaves `context` empty and adds no
|
|
34
|
+
device or precision override. Available acceleration depends on the selected
|
|
35
|
+
runtime distribution; see the shared guide's platform availability. Model
|
|
36
|
+
selection alone does not establish actual execution on any device.
|
|
37
|
+
|
|
38
|
+
## Prepare the checkpoint and generate an image
|
|
39
|
+
|
|
40
|
+
The single `model` resource is the official
|
|
41
|
+
[`sd_xl_base_1.0.safetensors` checkpoint](https://huggingface.co/stabilityai/stable-diffusion-xl-base-1.0/resolve/462165984030d82259a11f4367a4eed129e94a7b/sd_xl_base_1.0.safetensors)
|
|
42
|
+
at revision `462165984030d82259a11f4367a4eed129e94a7b`. It includes the text
|
|
43
|
+
encoders, denoiser and original VAE. Standalone base generation uses this one
|
|
44
|
+
resource; a refiner, separate VAE, LoRA or Python converter is unnecessary for
|
|
45
|
+
this selection. The upstream model carries the
|
|
46
|
+
[CreativeML Open RAIL++-M license](https://huggingface.co/stabilityai/stable-diffusion-xl-base-1.0/blob/462165984030d82259a11f4367a4eed129e94a7b/LICENSE.md).
|
|
47
|
+
|
|
48
|
+
The existing DBOPFS model store owns the authoritative download. The shared
|
|
49
|
+
[model-assets service](model-assets.md) prepares complete native working files
|
|
50
|
+
when the application loads the model. Installing the native runtime does not
|
|
51
|
+
download a second model copy.
|
|
52
|
+
|
|
53
|
+
This example extends the shared generation guide. It uses an existing `images`
|
|
54
|
+
accessor, DBOPFS-backed `modelStore`, application-selected `workingDirectory`
|
|
55
|
+
and request `signal`:
|
|
56
|
+
|
|
57
|
+
```javascript
|
|
58
|
+
import {createBrowserModelSource} from 'arcane-os/ai/browser-wasm';
|
|
59
|
+
import {prepareCoreModelAssets} from 'arcane-os/ai/core-model-assets';
|
|
60
|
+
|
|
61
|
+
const selected = (await images.inspect({signal})).models.find(
|
|
62
|
+
function selectedSDXL(model) {
|
|
63
|
+
return model.id === 'sdxl-base-1.0';
|
|
64
|
+
}
|
|
65
|
+
);
|
|
66
|
+
const resource = selected.resources.model;
|
|
67
|
+
const source = createBrowserModelSource({
|
|
68
|
+
id: selected.id,
|
|
69
|
+
files: [{name: resource.filename, url: resource.url}]
|
|
70
|
+
});
|
|
71
|
+
const stored = await modelStore.ensure(source, {signal});
|
|
72
|
+
const projection = await prepareCoreModelAssets({
|
|
73
|
+
workingDirectory,
|
|
74
|
+
members: source.files.map(function workingMember(member, index) {
|
|
75
|
+
return {path: member.name, file: stored.files[index]};
|
|
76
|
+
}),
|
|
77
|
+
signal
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
try {
|
|
81
|
+
await images.load({
|
|
82
|
+
model: selected.id,
|
|
83
|
+
assetProjectionId: projection.id,
|
|
84
|
+
resourcePaths: {model: resource.filename},
|
|
85
|
+
signal
|
|
86
|
+
});
|
|
87
|
+
} finally {
|
|
88
|
+
await projection.release();
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
const result = await images.generate({
|
|
92
|
+
model: selected.id,
|
|
93
|
+
prompt: 'An octopus librarian shelving tiny books inside a glass submarine.',
|
|
94
|
+
signal
|
|
95
|
+
});
|
|
96
|
+
// Display result.images using the shared guide's Blob preview example.
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
Change the prompt to request another scene. The model defaults to a
|
|
100
|
+
1024-by-1024 canvas; pass `parameters: {width, height}` to select other native
|
|
101
|
+
dimensions. Display or save every returned image using its actual width and
|
|
102
|
+
height. Each result contains the complete PNG Blob.
|
|
103
|
+
|
|
104
|
+
Successful loading retains the working projection in the native model owner.
|
|
105
|
+
Releasing the caller's preparation handle preserves that native retain.
|
|
106
|
+
Repeated generation reuses the loaded context; unload it when the application
|
|
107
|
+
no longer needs it. Follow the shared lifecycle contract for request
|
|
108
|
+
cancellation, state subscriptions, unloading and accessor closure.
|
|
109
|
+
|
|
110
|
+
## Defaults and operation scope
|
|
111
|
+
|
|
112
|
+
Only canvas dimensions are model-specific defaults here. Sampling parameters
|
|
113
|
+
remain owned by the selected native engine and can be supplied through
|
|
114
|
+
`parameters.sample_params`. At `master-929-3f8527a`, SDXL resolves the native
|
|
115
|
+
default sampler to Euler A and scheduler to discrete, with 20 steps and CFG 7.
|
|
116
|
+
These are native defaults; Stability AI's separate base-plus-refiner example
|
|
117
|
+
and Python pipeline have their own sampling configuration.
|
|
118
|
+
|
|
119
|
+
The selected engine has an explicit path for SDXL's embedded original VAE,
|
|
120
|
+
including its native convolution scaling. The model definition leaves that
|
|
121
|
+
treatment with the engine and supplies no guessed weight-type override or
|
|
122
|
+
separate VAE checkpoint. Output quality, speed and device behavior depend on
|
|
123
|
+
the actual selected runtime and generation parameters.
|
|
124
|
+
|
|
125
|
+
The SDK forwards the complete caller prompt to the native API. The engine
|
|
126
|
+
applies its own text conditioning and prompt-weighting syntax; see the shared
|
|
127
|
+
guide for native string-boundary limitations. The SDK adds no avatar wording,
|
|
128
|
+
style instructions or application-specific content.
|
|
129
|
+
|
|
130
|
+
This definition advertises `txt2img`. Whole-image editing, masked editing and
|
|
131
|
+
reference-image editing are separate operations with their own model and
|
|
132
|
+
runtime requirements. This base selection does not advertise those operations.
|
|
133
|
+
|
|
134
|
+
The model resource and standalone use follow the
|
|
135
|
+
[official model card](https://huggingface.co/stabilityai/stable-diffusion-xl-base-1.0/blob/462165984030d82259a11f4367a4eed129e94a7b/README.md).
|
|
136
|
+
Native defaults and embedded-VAE behavior follow the selected engine's
|
|
137
|
+
[request implementation](https://github.com/leejet/stable-diffusion.cpp/blob/master-929-3f8527a/src/pipeline/request.cpp),
|
|
138
|
+
[parameter initialization](https://github.com/leejet/stable-diffusion.cpp/blob/master-929-3f8527a/src/stable-diffusion.cpp)
|
|
139
|
+
and [model construction](https://github.com/leejet/stable-diffusion.cpp/blob/master-929-3f8527a/src/pipeline/model_builders.cpp).
|
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
# Native Whisper transcription
|
|
2
|
+
|
|
3
|
+
Arcane runs `whisper.cpp` through a persistent native helper. A selected model
|
|
4
|
+
stays loaded across recordings. The helper reports actual model readiness,
|
|
5
|
+
native progress and timestamped segments, and completes cancellation before
|
|
6
|
+
the model or recording files are reused. The browser's existing
|
|
7
|
+
`AI.fetchSTT(audioFile, signal)` and Core `speech.transcribe` routes retain the
|
|
8
|
+
application's selected model and cancellation signal.
|
|
9
|
+
|
|
10
|
+
## Select the runtime and model
|
|
11
|
+
|
|
12
|
+
An application's `arcane-app.json` selects native speech alongside its other
|
|
13
|
+
independent local runtimes:
|
|
14
|
+
|
|
15
|
+
```json
|
|
16
|
+
{
|
|
17
|
+
"native": {
|
|
18
|
+
"localAI": {
|
|
19
|
+
"runtimes": [
|
|
20
|
+
{
|
|
21
|
+
"id": "whisper.cpp",
|
|
22
|
+
"backend": "auto",
|
|
23
|
+
"models": ["whisper-small"]
|
|
24
|
+
}
|
|
25
|
+
]
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
The equivalent package configuration is `arcane-package.json`'s `localAI`.
|
|
32
|
+
Existing unrelated descriptor fields remain part of the application's file.
|
|
33
|
+
`models` defaults to an empty array, so selecting the runtime alone acquires no
|
|
34
|
+
model. A single selected model becomes `modelId`; multiple models require an
|
|
35
|
+
explicit selected `modelId` before transcription. The built-in `whisper-small`
|
|
36
|
+
descriptor selects the official `ggerganov/whisper.cpp` `ggml-small.bin` model.
|
|
37
|
+
An explicit model descriptor is `{id, path}` or `{id, url, filename?}`.
|
|
38
|
+
|
|
39
|
+
Default Windows x64 preparation acquires the selected complete upstream
|
|
40
|
+
Whisper 1.9.4/b5130 CUDA 12.4 and CPU distributions, FFmpeg 9.0.2 essentials,
|
|
41
|
+
the selected model, and Arcane's helper asset from the matching SDK numeric
|
|
42
|
+
release. The Windows helper includes its selected Visual C++ release runtime
|
|
43
|
+
DLLs beside the executable. Preparation shares matching installation jobs, observes cancellation,
|
|
44
|
+
and preserves the complete selected runtime trees. Model-selection changes
|
|
45
|
+
can reuse the already prepared runtime and matching models.
|
|
46
|
+
|
|
47
|
+
With `backend: "auto"`, accelerated runtimes are attempted before CPU. Failure
|
|
48
|
+
to load an accelerated runtime falls back to the prepared CPU runtime. If
|
|
49
|
+
native accelerated inference fails, Arcane drains that context and retries the
|
|
50
|
+
complete decoded recording once on CPU with the same model. Cancellation,
|
|
51
|
+
input errors and observer errors do not trigger this retry. Explicit `cuda`,
|
|
52
|
+
`metal` or `cpu` selections retain that chosen backend.
|
|
53
|
+
|
|
54
|
+
During that accepted request's internal recovery, the engine reports
|
|
55
|
+
`state: "recovering"`, `loaded: false`, and `busy: true`, with the same selected
|
|
56
|
+
provider and model. It accepts no new transcription while the old context
|
|
57
|
+
drains and the CPU context loads. Only actual CPU readiness restores `ready`.
|
|
58
|
+
An explicitly unloaded, replaced, or closed model remains a cancellation
|
|
59
|
+
boundary; internal recovery preserves the original request and its signal.
|
|
60
|
+
|
|
61
|
+
`requestedBackend` identifies a preference. `observedBackend` and
|
|
62
|
+
`backendEvidence` report native initialization evidence when available; they
|
|
63
|
+
do not claim that every computation runs on the GPU. No driver or compiler is
|
|
64
|
+
installed on application startup.
|
|
65
|
+
|
|
66
|
+
## Core and development integration
|
|
67
|
+
|
|
68
|
+
Development and generated native Core entries compose a
|
|
69
|
+
[`createSpeechService`](./native-speech.md) with the selected Whisper engine.
|
|
70
|
+
Speech preparation and model loading proceed independently of the page,
|
|
71
|
+
chat and image services. Core publishes retained `speech.state` and per-request
|
|
72
|
+
`speech.progress`; transcription availability follows the selected engine's
|
|
73
|
+
actual loaded state. The shared service remains usable when only STT is
|
|
74
|
+
selected, through `transcriptionAvailable` and `roles.stt`.
|
|
75
|
+
|
|
76
|
+
The native bundle includes the helper, runtime libraries, decoder and selected
|
|
77
|
+
model under its runtime directory beside the application executable. Those
|
|
78
|
+
resources stay in the application bundle. They are not embedded into one
|
|
79
|
+
Windows executable. The compiler and matching upstream headers are build-time
|
|
80
|
+
requirements for the first-party helper only.
|
|
81
|
+
|
|
82
|
+
Applications may continue using the existing Core call:
|
|
83
|
+
|
|
84
|
+
```js
|
|
85
|
+
const transcript = await client.speech.transcribe({
|
|
86
|
+
audioBase64,
|
|
87
|
+
mimeType: 'audio/webm',
|
|
88
|
+
model: 'whisper-small'
|
|
89
|
+
}, {signal});
|
|
90
|
+
console.log(transcript.text);
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
FFmpeg decodes the complete recorded media into mono 16 kHz PCM at the native
|
|
94
|
+
transport boundary. The original recording remains untouched during the
|
|
95
|
+
operation. Arcane imposes no recording-duration cap and does not trim the
|
|
96
|
+
transcript. The upstream inference API's signed-integer sample argument is
|
|
97
|
+
reported honestly if a recording cannot fit that native call; no audio is
|
|
98
|
+
silently shortened. Complete operation files are removed only after decoder
|
|
99
|
+
and native inference cleanup have completed.
|
|
100
|
+
|
|
101
|
+
## Public engine API
|
|
102
|
+
|
|
103
|
+
```js
|
|
104
|
+
import {createWhisperRuntime} from 'arcane-os/local-ai/whisper';
|
|
105
|
+
|
|
106
|
+
const stt = createWhisperRuntime({
|
|
107
|
+
runtime: preparedRuntime,
|
|
108
|
+
modelId: 'whisper-small',
|
|
109
|
+
temporaryDirectory: applicationSpeechDirectory,
|
|
110
|
+
onEvent: reportDiagnostic
|
|
111
|
+
});
|
|
112
|
+
const unsubscribe = stt.subscribe(renderModelState);
|
|
113
|
+
await stt.load({signal});
|
|
114
|
+
try {
|
|
115
|
+
const result = await stt.transcribe({audioBase64, mimeType, model: 'whisper-small'}, {
|
|
116
|
+
signal,
|
|
117
|
+
onProgress: renderTranscriptionProgress
|
|
118
|
+
});
|
|
119
|
+
console.log(result.text, result.segments);
|
|
120
|
+
} finally {
|
|
121
|
+
unsubscribe();
|
|
122
|
+
await stt.close();
|
|
123
|
+
}
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
`createWhisperRuntime(options)` performs no model load. `options.runtime` is a
|
|
127
|
+
prepared descriptor returned by `ensureLocalAIRuntimes`; alternatively,
|
|
128
|
+
`options.prepare({signal, onEvent})` lazily returns that descriptor during
|
|
129
|
+
`load`. `temporaryDirectory` is required and belongs to the application.
|
|
130
|
+
|
|
131
|
+
| Member | Contract |
|
|
132
|
+
| --- | --- |
|
|
133
|
+
| `current()` | Returns `providerId`, selected `modelId`, lifecycle `state`, `loaded`, `busy`, correlated `requestId` or null, requested/observed backend, evidence and any error. |
|
|
134
|
+
| `subscribe(listener, {replay = true, signal} = {})` | Immediately replays current state by default and returns an unsubscribe function. |
|
|
135
|
+
| `load({modelId, signal} = {})` | Prepares the runtime if necessary, then waits for actual native model readiness. With no selected model it remains unloaded. |
|
|
136
|
+
| `transcribe(request, {signal, onProgress, requestId} = {})` | Consumes the complete `audioBase64` recording. Optional `language` defaults to native automatic detection; `translate: true` selects Whisper's translation operation. `model` must match the loaded model. Optional `requestId` carries the caller's existing operation identity through lifecycle snapshots as separate control information; it does not alter the recording or request payload. |
|
|
137
|
+
| `unload()` | Cancels and joins active load/transcription, releases the native context, and permits a later explicit load. |
|
|
138
|
+
| `close()` | Joins shutdown and disposes this engine permanently. Repeated calls share the same shutdown. |
|
|
139
|
+
|
|
140
|
+
One retained model processes one recording at a time; a simultaneous request
|
|
141
|
+
receives `WHISPER_BUSY`. Model changes use `await stt.unload()` followed by
|
|
142
|
+
`stt.load({modelId})`. The `ready` state remains `loaded: true` with `busy: true`
|
|
143
|
+
during ordinary inference. Progress records include the native event plus
|
|
144
|
+
`backend` and `attempt`, so a CPU retry remains distinguishable. Segment text
|
|
145
|
+
is preserved in order. The final result is
|
|
146
|
+
`{text, language, duration, segments: [{index, start, end, text}]}`; times are
|
|
147
|
+
seconds and are native segment timestamps, not invented word timestamps.
|
|
148
|
+
|
|
149
|
+
Full process diagnostics stay with `onEvent` and native process failures.
|
|
150
|
+
Applications should use their selected inspection surface for those details
|
|
151
|
+
and keep ordinary user status concise.
|
|
152
|
+
An error snapshot retains the failed operation's `requestId` after `busy`
|
|
153
|
+
becomes false, allowing the caller to correlate its actual pending response.
|
|
154
|
+
A new request or model load supersedes that identity; unload and close clear
|
|
155
|
+
it. No recording or request payload is retained by this correlation field.
|
|
156
|
+
Progress callbacks may cancel through the supplied controller and return;
|
|
157
|
+
they must not await the same transcription or its shutdown from inside its
|
|
158
|
+
own output callback. Await those operations in the initiating owner instead.
|
|
159
|
+
|
|
160
|
+
## Explicit runtimes and helper builds
|
|
161
|
+
|
|
162
|
+
Windows, Linux and macOS host integration uses the same public engine and
|
|
163
|
+
portable helper source. Automatic archive selection currently provides the
|
|
164
|
+
Windows x64 distribution above. Other targets supply their complete selected
|
|
165
|
+
runtime, decoder and compiled helper paths; their native execution requires
|
|
166
|
+
verification on that actual platform. Android requires a host adapter for
|
|
167
|
+
its process and library lifecycle.
|
|
168
|
+
|
|
169
|
+
An explicit runtime requirement can supply:
|
|
170
|
+
|
|
171
|
+
```js
|
|
172
|
+
{
|
|
173
|
+
id: 'whisper.cpp',
|
|
174
|
+
version: '1.9.4',
|
|
175
|
+
backend: 'auto',
|
|
176
|
+
variants: [
|
|
177
|
+
{backend: 'cuda', root: cudaRuntimeDirectory, libraryDirectory: cudaLibraryDirectory},
|
|
178
|
+
{backend: 'cpu', root: cpuRuntimeDirectory, libraryDirectory: cpuLibraryDirectory}
|
|
179
|
+
],
|
|
180
|
+
helperExecutable,
|
|
181
|
+
helperRoot,
|
|
182
|
+
decoderExecutable,
|
|
183
|
+
decoderRoot,
|
|
184
|
+
models: [{id: 'whisper-small', path: selectedModelPath}],
|
|
185
|
+
modelId: 'whisper-small'
|
|
186
|
+
}
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
`libraryDirectory` belongs to its complete variant root. `decoderExecutable`
|
|
190
|
+
belongs to `decoderRoot` (which defaults to its containing directory).
|
|
191
|
+
`helperExecutable` similarly belongs to `helperRoot`; its complete directory
|
|
192
|
+
tree is retained, including app-local native dependencies.
|
|
193
|
+
Preparation copies those selected resources into its owned installation, and
|
|
194
|
+
native packaging projects their paths relative to the application bundle.
|
|
195
|
+
`helperUrl`, `helperVersion`, and `decoderUrl` allow an explicitly selected
|
|
196
|
+
published distribution instead of local paths. Dependency permissions remain
|
|
197
|
+
the caller's responsibility.
|
|
198
|
+
|
|
199
|
+
```js
|
|
200
|
+
import {buildWhisperHelper} from 'arcane-os/local-ai/whisper/build';
|
|
201
|
+
|
|
202
|
+
const helper = await buildWhisperHelper({
|
|
203
|
+
runtime: {directory: runtimeLibraryDirectory, sourceDirectory: matchingUpstreamSource},
|
|
204
|
+
outputRoot: helperBuildDirectory,
|
|
205
|
+
signal,
|
|
206
|
+
onEvent: reportDiagnostic
|
|
207
|
+
});
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
The builder runs CMake configure/build/install against the selected matching
|
|
211
|
+
public upstream headers and runtime. Optional `cmake`, `generator`,
|
|
212
|
+
`cmakeArgs` and `env` select the existing toolchain. It returns the helper
|
|
213
|
+
`executable`, platform, architecture, runtime directory and complete build
|
|
214
|
+
diagnostics. The helper's documented process contract is in
|
|
215
|
+
[`native/README.md`](../../src/local-ai/whisper/native/README.md).
|
|
216
|
+
On MSVC, CMake's installed runtime-library module adds release CRT files to
|
|
217
|
+
the helper's `bin/` directory. `-DMSVC_REDIST_DIR=...` selects an existing
|
|
218
|
+
redistributable directory when needed. Windows provides the Universal CRT;
|
|
219
|
+
compiler tools and debug runtimes stay outside the application bundle.
|
|
@@ -0,0 +1,263 @@
|
|
|
1
|
+
# MCP STDIO server
|
|
2
|
+
|
|
3
|
+
Expose application-owned tools and static resources to an MCP client with
|
|
4
|
+
`arcane-os/mcp/stdio`. This optional Node entrypoint owns one JSON-RPC session,
|
|
5
|
+
UTF-8 newline framing, discovery, request correlation, cooperative cancellation,
|
|
6
|
+
and graceful drain. The application owns its launch command, tool definitions,
|
|
7
|
+
resource contents, business behavior, and underlying service lifetime.
|
|
8
|
+
|
|
9
|
+
Availability: Node on Windows, Linux, and macOS, using the package's supported
|
|
10
|
+
Node version. Android requires a host that supplies the same Node stream and
|
|
11
|
+
process environment. Importing the entrypoint starts no process or listener.
|
|
12
|
+
It is separate from the native Core `arcane/1` transport and its Content-Length
|
|
13
|
+
frames; the two stream formats are not interchangeable.
|
|
14
|
+
|
|
15
|
+
## createMcpStdioServer
|
|
16
|
+
|
|
17
|
+
```javascript
|
|
18
|
+
import {createMcpStdioServer} from 'arcane-os/mcp/stdio';
|
|
19
|
+
|
|
20
|
+
const menu = {
|
|
21
|
+
content: 'Moon soup\nExtra tentacles available.',
|
|
22
|
+
message: {chef: 'Octopus', open: true}
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
const server = createMcpStdioServer(
|
|
26
|
+
{
|
|
27
|
+
serverInfo: {name: 'moon-pantry', version: '1.0.0'},
|
|
28
|
+
instructions: 'Ask moon.menu for the complete lunar dinner menu.',
|
|
29
|
+
tools: [
|
|
30
|
+
{
|
|
31
|
+
name: 'moon.menu',
|
|
32
|
+
description: 'Read the complete lunar dinner menu.',
|
|
33
|
+
inputSchema: {type: 'object'},
|
|
34
|
+
annotations: {readOnlyHint: true},
|
|
35
|
+
handler: readMenu
|
|
36
|
+
}
|
|
37
|
+
],
|
|
38
|
+
resources: [
|
|
39
|
+
{
|
|
40
|
+
uri: 'pantry://menu',
|
|
41
|
+
name: 'menu',
|
|
42
|
+
title: 'Lunar dinner menu',
|
|
43
|
+
mimeType: 'application/json',
|
|
44
|
+
handler: readMenuResource
|
|
45
|
+
}
|
|
46
|
+
]
|
|
47
|
+
}
|
|
48
|
+
);
|
|
49
|
+
|
|
50
|
+
function readMenu(args, {signal}) {
|
|
51
|
+
signal.throwIfAborted();
|
|
52
|
+
return {
|
|
53
|
+
content: [{type: 'text', text: JSON.stringify(menu)}],
|
|
54
|
+
structuredContent: menu
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function readMenuResource({uri}, {signal}) {
|
|
59
|
+
signal.throwIfAborted();
|
|
60
|
+
return {
|
|
61
|
+
contents: [{uri, mimeType: 'application/json', text: JSON.stringify(menu)}]
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
server.start();
|
|
66
|
+
try {
|
|
67
|
+
await server.closed;
|
|
68
|
+
} catch {
|
|
69
|
+
// The adapter already sent its diagnostic to stderr.
|
|
70
|
+
process.exitCode = 1;
|
|
71
|
+
}
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
Save this in the consuming application and configure the MCP client's subprocess
|
|
75
|
+
command as `node` with that application's script path. Client configuration
|
|
76
|
+
file formats and process launch policy remain client-owned. Change `menu` to
|
|
77
|
+
change the complete result; no model, repository, or native host is needed for
|
|
78
|
+
this example.
|
|
79
|
+
|
|
80
|
+
The application explicitly converts its domain record into an MCP result.
|
|
81
|
+
The SDK does not guess from `content`, `message`, or other domain field names.
|
|
82
|
+
The serialized text in this example is an application-selected representation;
|
|
83
|
+
the SDK never adds it automatically.
|
|
84
|
+
|
|
85
|
+
### Parameters
|
|
86
|
+
|
|
87
|
+
`createMcpStdioServer(options)` returns a server object synchronously.
|
|
88
|
+
|
|
89
|
+
| Option | Contract |
|
|
90
|
+
| --- | --- |
|
|
91
|
+
| `serverInfo` | Required MCP implementation object with `name` and `version` strings. Optional MCP implementation fields are passed through. |
|
|
92
|
+
| `instructions` | Optional complete instruction string returned by initialization. |
|
|
93
|
+
| `tools` | Optional static array; each entry is a complete MCP tool descriptor plus `handler(args, context)`. Defaults to `[]`. Tool names identify handlers exactly and must be unique. |
|
|
94
|
+
| `resources` | Optional static array; each entry is a complete MCP resource descriptor plus `handler(params, context)`. Defaults to `[]`. Each resource requires `uri` and `name`; URIs identify handlers exactly and must be unique. |
|
|
95
|
+
| `onDiagnostic` | Optional error observer. Omit it for complete error text on the selected stderr stream. It may return a promise, which participates in drain. Observer failures are reported to stderr and retained by `closed`. |
|
|
96
|
+
|
|
97
|
+
The factory captures catalog entries at construction. Discovery preserves
|
|
98
|
+
descriptor fields, including authored schemas, descriptions, annotations,
|
|
99
|
+
icons, and metadata; only the local `handler` function is omitted. No tool
|
|
100
|
+
arguments, descriptions, schemas, resources, or result content are injected,
|
|
101
|
+
trimmed, rewritten, or shortened. Supply JSON-serializable protocol values.
|
|
102
|
+
|
|
103
|
+
The SDK checks the request envelope and required result container. It does not
|
|
104
|
+
provide a general JSON Schema evaluator. The application supplies a valid
|
|
105
|
+
`inputSchema` object, owns the handler's input semantics, and ensures any
|
|
106
|
+
declared `outputSchema` matches its structured result. This adapter does not
|
|
107
|
+
implement task-augmented execution; descriptors must describe ordinary tool
|
|
108
|
+
execution.
|
|
109
|
+
|
|
110
|
+
### Handler inputs and results
|
|
111
|
+
|
|
112
|
+
Tool handlers receive `params.arguments` unchanged. Omitted arguments become
|
|
113
|
+
an empty object as the protocol's no-argument case. Resource handlers receive
|
|
114
|
+
the complete `resources/read` params object, including its exact `uri` and
|
|
115
|
+
any metadata. A handler may return its result directly or return a promise.
|
|
116
|
+
|
|
117
|
+
Each handler receives a context with:
|
|
118
|
+
|
|
119
|
+
- `signal`: its own request's `AbortSignal`;
|
|
120
|
+
- `requestId`: the original string or integer ID, including numeric zero;
|
|
121
|
+
- `method`: `tools/call` or `resources/read`;
|
|
122
|
+
- `params`: the complete request params object;
|
|
123
|
+
- `clientInfo` and `clientCapabilities`: the client's initialization objects.
|
|
124
|
+
|
|
125
|
+
A tool handler returns an explicit MCP `CallToolResult`, including a `content`
|
|
126
|
+
array and optional `structuredContent`, `isError`, `_meta`, or other protocol
|
|
127
|
+
fields. All supplied content blocks and JSON fields pass through. Resource
|
|
128
|
+
handlers return an explicit `ReadResourceResult` with a `contents` array;
|
|
129
|
+
complete text or already encoded binary resource contents pass through.
|
|
130
|
+
The application creates these protocol result objects deliberately. A plain
|
|
131
|
+
domain object with `content: 'some text'` is not a `CallToolResult` and produces
|
|
132
|
+
an internal protocol error rather than being silently converted.
|
|
133
|
+
|
|
134
|
+
For a domain failure, return a complete `CallToolResult` with `isError: true`.
|
|
135
|
+
An ordinary exception thrown by a tool handler becomes an `isError: true`
|
|
136
|
+
text result containing the exception's complete message, and the complete
|
|
137
|
+
exception is also reported through diagnostics. Throw `McpProtocolError` for
|
|
138
|
+
an explicit JSON-RPC failure. Other resource or adapter exceptions produce
|
|
139
|
+
`-32603` and a diagnostic; malformed result containers and non-serializable
|
|
140
|
+
results also produce `-32603`.
|
|
141
|
+
Custom fields on an ordinary thrown error remain in diagnostics unless the
|
|
142
|
+
application explicitly maps its known outcome to `structuredContent` in an
|
|
143
|
+
`isError: true` tool result.
|
|
144
|
+
|
|
145
|
+
## start
|
|
146
|
+
|
|
147
|
+
`server.start({input, output, error})` attaches to caller-owned Node streams and
|
|
148
|
+
returns the same server. Defaults are `process.stdin`, `process.stdout`, and
|
|
149
|
+
`process.stderr`. Start each server exactly once with an open readable input and
|
|
150
|
+
open writable output/diagnostic streams. Output and diagnostics must be separate
|
|
151
|
+
streams. Byte-mode input preserves split UTF-8 sequences; a stream already
|
|
152
|
+
decoded into strings remains responsible for its own decoding.
|
|
153
|
+
|
|
154
|
+
Messages are processed as their newline arrives, without waiting for EOF. A
|
|
155
|
+
final complete JSON record is also accepted when EOF arrives without its final
|
|
156
|
+
newline. LF and CRLF transport endings are accepted. JSON escaping preserves
|
|
157
|
+
embedded line breaks in the decoded payload. Malformed JSON receives `-32700`;
|
|
158
|
+
its complete original line is included in diagnostics. Invalid UTF-8 fails at
|
|
159
|
+
the decoder and closes the transport with the complete affected input chunks
|
|
160
|
+
and pending decoded text in its diagnostic error.
|
|
161
|
+
|
|
162
|
+
Requests execute independently, including overlapping calls to one tool.
|
|
163
|
+
Only complete output writes are ordered, waiting for each Node write callback
|
|
164
|
+
before sending the next record. An error, close, or finish event during a pending
|
|
165
|
+
output or diagnostic write settles that write as failed even when its callback
|
|
166
|
+
never arrives; a later callback cannot settle it again. The application decides
|
|
167
|
+
whether its own data access requires narrower serialization. The adapter adds
|
|
168
|
+
no process-wide queue.
|
|
169
|
+
|
|
170
|
+
Only JSON-RPC messages are written to `output`. Application handlers and their
|
|
171
|
+
dependencies must also reserve stdout for protocol traffic; use stderr or the
|
|
172
|
+
application's diagnostic owner for logs. The adapter does not replace global
|
|
173
|
+
console functions.
|
|
174
|
+
|
|
175
|
+
## cancel
|
|
176
|
+
|
|
177
|
+
`server.cancel(requestId, reason)` requests cooperative cancellation and returns
|
|
178
|
+
`true` only when an active cancellable handler was signalled. String and numeric
|
|
179
|
+
IDs remain distinct. Unknown, completed, already cancelled, and initialization
|
|
180
|
+
requests return `false`. The same behavior handles client
|
|
181
|
+
`notifications/cancelled` messages. Late results from cancelled work have no
|
|
182
|
+
protocol response, as described by the
|
|
183
|
+
[MCP cancellation contract](https://modelcontextprotocol.io/specification/2025-11-25/basic/utilities/cancellation).
|
|
184
|
+
|
|
185
|
+
The handler must pass its signal to its actual I/O or computation and release
|
|
186
|
+
owned resources. Signalling does not prove external work stopped. The adapter
|
|
187
|
+
retains the handler promise until it really settles; a handler that ignores
|
|
188
|
+
cancellation can keep drain and close pending.
|
|
189
|
+
|
|
190
|
+
## drain
|
|
191
|
+
|
|
192
|
+
`await server.drain()` waits for accepted handlers, diagnostic observers, and
|
|
193
|
+
output write callbacks to settle. It does not stop input or abort requests, so
|
|
194
|
+
newly accepted work can extend the wait. Use `close()` when input must first
|
|
195
|
+
stop. Tool and request errors are delivered as protocol outcomes; transport and
|
|
196
|
+
diagnostic delivery failures are retained by `closed`.
|
|
197
|
+
|
|
198
|
+
## close
|
|
199
|
+
|
|
200
|
+
`await server.close({cancelPending: false})` stops reading, pauses the input,
|
|
201
|
+
drains accepted work and writes, detaches the adapter's stream listeners, and
|
|
202
|
+
settles `closed`. It is idempotent. Normal input EOF follows this same graceful
|
|
203
|
+
path. Explicit `cancelPending: true` aborts pending work instead; repeated close
|
|
204
|
+
may request cancellation while an earlier graceful close is draining.
|
|
205
|
+
|
|
206
|
+
Stream failures request cancellation, report diagnostics, and reject `closed`
|
|
207
|
+
after owned work settles. An input closed without EOF is a transport failure.
|
|
208
|
+
The adapter does not end or destroy caller streams, terminate the process,
|
|
209
|
+
install process signal handlers, start an application host, or close
|
|
210
|
+
application services. The launcher owns those actions after the adapter drains.
|
|
211
|
+
|
|
212
|
+
## closed
|
|
213
|
+
|
|
214
|
+
`server.closed` is a promise for actual session cleanup. It resolves after a
|
|
215
|
+
normal close or EOF and rejects with an `AggregateError` when transport or
|
|
216
|
+
diagnostic delivery failed. Observe it even when the initiating caller continues
|
|
217
|
+
other work. Request failures alone do not close the session.
|
|
218
|
+
|
|
219
|
+
## MCP_PROTOCOL_VERSION
|
|
220
|
+
|
|
221
|
+
`MCP_PROTOCOL_VERSION` is exactly `'2025-11-25'`. Initialization always returns
|
|
222
|
+
that supported version. A client proposing another version must accept this
|
|
223
|
+
profile or disconnect. This is the initialize/initialized STDIO profile;
|
|
224
|
+
support for later discovery-based profiles is not implied. See the normative
|
|
225
|
+
[lifecycle](https://modelcontextprotocol.io/specification/2025-11-25/basic/lifecycle)
|
|
226
|
+
and [STDIO transport](https://modelcontextprotocol.io/specification/2025-11-25/basic/transports).
|
|
227
|
+
|
|
228
|
+
Supported requests are `initialize`, `ping`, `tools/list`, `tools/call`,
|
|
229
|
+
`resources/list`, `resources/read`, and `resources/templates/list` (an empty
|
|
230
|
+
template catalog). Tools and resources capabilities are advertised when their
|
|
231
|
+
catalogs are nonempty. Lists return their complete catalogs without pagination;
|
|
232
|
+
supplying a cursor receives `-32602`. Send `notifications/initialized` after
|
|
233
|
+
the initialization response before ordinary operations.
|
|
234
|
+
|
|
235
|
+
There are no prompts, subscriptions, list-change events, MCP logging requests,
|
|
236
|
+
server-to-client requests, sampling, elicitation, task augmentation, HTTP
|
|
237
|
+
transport, or automatic client configuration. Unknown methods return `-32601`;
|
|
238
|
+
notifications never receive responses. JSON-RPC batches are not part of this
|
|
239
|
+
profile. IDs must be strings or integers and are unique for the session.
|
|
240
|
+
|
|
241
|
+
The result shapes follow the official
|
|
242
|
+
[tool](https://modelcontextprotocol.io/specification/2025-11-25/server/tools)
|
|
243
|
+
and [resource](https://modelcontextprotocol.io/specification/2025-11-25/server/resources)
|
|
244
|
+
contracts. This narrow adapter does not claim every optional MCP capability.
|
|
245
|
+
|
|
246
|
+
## McpProtocolError
|
|
247
|
+
|
|
248
|
+
`new McpProtocolError(code, message, data)` represents an explicit protocol
|
|
249
|
+
failure. `code` is a JSON-RPC integer, `message` is its complete string, and
|
|
250
|
+
optional `data` is JSON-serializable protocol detail. It extends `Error` and
|
|
251
|
+
exposes `name`, `code`, `message`, and optional `data`.
|
|
252
|
+
|
|
253
|
+
The adapter uses `-32700` for malformed JSON, `-32600` for invalid requests,
|
|
254
|
+
`-32601` for unknown methods, `-32602` for malformed params or unknown tools,
|
|
255
|
+
`-32603` for internal failures, and `-32002` for a missing resource. Its
|
|
256
|
+
`-32000` lifecycle error requests initialization before ordinary operations.
|
|
257
|
+
|
|
258
|
+
## Evidence boundary
|
|
259
|
+
|
|
260
|
+
`test/mcp-stdio-server.test.mjs` contains focused in-memory stream fixtures for
|
|
261
|
+
these contracts. Authored fixtures and source review do not establish an
|
|
262
|
+
executed client integration or platform run. Run local tests only under the
|
|
263
|
+
applicable explicit testing or selected-output authority.
|