arcane-os 0.40.0 → 0.41.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 +34 -0
- package/NOTICE +3 -0
- package/README.md +1 -1
- package/browser-runtime/ai/browser-speech.mjs +4 -1
- package/browser-runtime/ai/digitalocean-speech.mjs +2 -468
- package/browser-runtime/ai/speech-worker-runtime.mjs +2 -3
- package/browser-runtime/ai/twin-cloud-speech.mjs +587 -0
- package/browser-runtime/dependencies/js-queue/licence.md +21 -0
- package/browser-runtime/dependencies/js-queue/package.json +100 -0
- package/browser-runtime/dependencies/js-queue/queue.js +78 -0
- package/docs/reference/ai/browser-speech.md +60 -14
- package/docs/reference/inventory/package-api.json +18 -4
- package/docs/reference/pwa.md +16 -1
- package/docs/reference/runtime-components.md +14 -4
- package/docs/reference/sdk-api.md +75 -17
- package/package.json +2 -1
- package/runtime/arcane/components/speech.html +17 -18
- package/src/dev-server.mjs +19 -11
- package/src/import-map.mjs +20 -1
- package/src/packager/core.mjs +8 -5
- package/src/toolchain.mjs +11 -1
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "js-queue",
|
|
3
|
+
"version": "3.1.1",
|
|
4
|
+
"description": "A tiny FIFO task queue with explicit flow control for Node and browsers",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./queue.js",
|
|
7
|
+
"module": "./queue.js",
|
|
8
|
+
"browser": "./queue.js",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"import": "./queue.js",
|
|
12
|
+
"require": "./queue.js",
|
|
13
|
+
"default": "./queue.js"
|
|
14
|
+
},
|
|
15
|
+
"./queue": {
|
|
16
|
+
"import": "./queue.js",
|
|
17
|
+
"require": "./queue.js"
|
|
18
|
+
},
|
|
19
|
+
"./queue.js": {
|
|
20
|
+
"import": "./queue.js",
|
|
21
|
+
"require": "./queue.js"
|
|
22
|
+
},
|
|
23
|
+
"./queue-vanilla.js": "./queue-vanilla.js",
|
|
24
|
+
"./stack": {
|
|
25
|
+
"import": "./stack.js",
|
|
26
|
+
"require": "./stack.js"
|
|
27
|
+
},
|
|
28
|
+
"./stack.js": {
|
|
29
|
+
"import": "./stack.js",
|
|
30
|
+
"require": "./stack.js"
|
|
31
|
+
},
|
|
32
|
+
"./package.json": "./package.json"
|
|
33
|
+
},
|
|
34
|
+
"files": [
|
|
35
|
+
"queue.js",
|
|
36
|
+
"queue-vanilla.js",
|
|
37
|
+
"stack.js",
|
|
38
|
+
"assets/js-queue-header.png",
|
|
39
|
+
"assets/vanilla-test-header.png",
|
|
40
|
+
"assets/benchmark-construct.svg",
|
|
41
|
+
"assets/benchmark-tasks.svg",
|
|
42
|
+
"README.md",
|
|
43
|
+
"CHANGELOG.md",
|
|
44
|
+
"MIGRATION.md",
|
|
45
|
+
"licence.md"
|
|
46
|
+
],
|
|
47
|
+
"sideEffects": [
|
|
48
|
+
"./queue-vanilla.js"
|
|
49
|
+
],
|
|
50
|
+
"engines": {
|
|
51
|
+
"node": ">=22.13.0"
|
|
52
|
+
},
|
|
53
|
+
"scripts": {
|
|
54
|
+
"test": "node ./scripts/test.js",
|
|
55
|
+
"test:unit": "node ./scripts/test.js unit",
|
|
56
|
+
"test:functional": "node ./scripts/test.js functional",
|
|
57
|
+
"test:behavioral": "node ./scripts/test.js behavioral",
|
|
58
|
+
"test:integration": "node ./scripts/test.js integration",
|
|
59
|
+
"test:regression": "node ./scripts/test.js regression",
|
|
60
|
+
"test:core": "node ./scripts/test.js core",
|
|
61
|
+
"test:playground": "node ./scripts/test.js playground",
|
|
62
|
+
"test:package": "node ./scripts/test.js package",
|
|
63
|
+
"test:consumer": "node ./scripts/test.js consumer",
|
|
64
|
+
"test:server": "node ./scripts/test.js server",
|
|
65
|
+
"test:docs": "node ./scripts/test.js docs",
|
|
66
|
+
"coverage": "vanilla-test coverage",
|
|
67
|
+
"coverage:node": "vanilla-test coverage node",
|
|
68
|
+
"coverage:chrome": "vanilla-test coverage chrome",
|
|
69
|
+
"benchmark": "node --expose-gc ./scripts/benchmark.js",
|
|
70
|
+
"start": "node ./scripts/serve.js",
|
|
71
|
+
"check": "npm test && npm run coverage:node"
|
|
72
|
+
},
|
|
73
|
+
"repository": {
|
|
74
|
+
"type": "git",
|
|
75
|
+
"url": "git+https://github.com/RIAEvangelist/js-queue.git"
|
|
76
|
+
},
|
|
77
|
+
"keywords": [
|
|
78
|
+
"queue",
|
|
79
|
+
"fifo",
|
|
80
|
+
"task-queue",
|
|
81
|
+
"flow-control",
|
|
82
|
+
"async",
|
|
83
|
+
"browser",
|
|
84
|
+
"node",
|
|
85
|
+
"esm",
|
|
86
|
+
"commonjs"
|
|
87
|
+
],
|
|
88
|
+
"author": "Brandon Nozaki Miller",
|
|
89
|
+
"license": "MIT",
|
|
90
|
+
"bugs": {
|
|
91
|
+
"url": "https://github.com/RIAEvangelist/js-queue/issues"
|
|
92
|
+
},
|
|
93
|
+
"homepage": "https://riaevangelist.github.io/js-queue/",
|
|
94
|
+
"dependencies": {
|
|
95
|
+
"easy-stack": "2.1.0"
|
|
96
|
+
},
|
|
97
|
+
"devDependencies": {
|
|
98
|
+
"vanilla-test": "2.1.1"
|
|
99
|
+
}
|
|
100
|
+
}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
function validateTasks(tasks){
|
|
2
|
+
const invalidIndex=tasks.findIndex(task=>typeof task !== 'function');
|
|
3
|
+
if(invalidIndex !== -1){
|
|
4
|
+
throw new TypeError(`Queue task at index ${invalidIndex} must be a function.`);
|
|
5
|
+
}
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
class Queue{
|
|
9
|
+
#contents=[];
|
|
10
|
+
#running=false;
|
|
11
|
+
|
|
12
|
+
constructor(){
|
|
13
|
+
this.autoRun=true;
|
|
14
|
+
this.stop=false;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
add(...tasks){
|
|
18
|
+
validateTasks(tasks);
|
|
19
|
+
this.#contents.push(...tasks);
|
|
20
|
+
|
|
21
|
+
if(!this.#running && !this.stop && this.autoRun){
|
|
22
|
+
this.next();
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
return this;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
next(){
|
|
29
|
+
if(this.stop || this.#contents.length === 0){
|
|
30
|
+
this.#running=false;
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
this.#running=true;
|
|
35
|
+
const task=this.#contents.shift();
|
|
36
|
+
|
|
37
|
+
try{
|
|
38
|
+
task.call(this);
|
|
39
|
+
}catch(error){
|
|
40
|
+
this.#running=false;
|
|
41
|
+
throw error;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
clear(){
|
|
46
|
+
this.#contents=[];
|
|
47
|
+
return this.#contents;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
get contents(){
|
|
51
|
+
return this.#contents;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
set contents(tasks){
|
|
55
|
+
if(!Array.isArray(tasks)){
|
|
56
|
+
throw new TypeError('Queue contents must be an array of functions.');
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
validateTasks(tasks);
|
|
60
|
+
this.#contents=tasks;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
get running(){
|
|
64
|
+
return this.#running;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
get size(){
|
|
68
|
+
return this.#contents.length;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
Queue.Queue=Queue;
|
|
73
|
+
|
|
74
|
+
export {
|
|
75
|
+
Queue as default,
|
|
76
|
+
Queue,
|
|
77
|
+
Queue as 'module.exports'
|
|
78
|
+
};
|
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
# Browser speech providers
|
|
2
2
|
|
|
3
3
|
`arcane-os/ai/browser-speech` supplies caller-selected Whisper speech-to-text,
|
|
4
|
-
Kokoro text-to-speech, and
|
|
4
|
+
Kokoro text-to-speech, and TWiN Cloud text-to-speech providers. Its local path
|
|
5
5
|
provides artifact storage, live module routing, role Workers, provider/2
|
|
6
6
|
adapters, bounded parallel TTS synthesis, audio normalization, cancellation,
|
|
7
7
|
and cleanup.
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
<a id="digitalocean-fal-text-to-speech"></a>
|
|
10
|
+
|
|
11
|
+
## TWiN Cloud text-to-speech
|
|
10
12
|
|
|
11
13
|
Use the same AI speech queue with an explicitly selected remote provider.
|
|
12
14
|
The application supplies its current DigitalOcean inference key through a
|
|
@@ -14,17 +16,18 @@ function returning a string or promise; never place that key in source, logs, or
|
|
|
14
16
|
This example receives an existing `ai` and application-owned key reader:
|
|
15
17
|
|
|
16
18
|
```javascript
|
|
17
|
-
import {
|
|
19
|
+
import {createTwinCloudTTSProvider} from 'arcane-os/ai/browser-speech';
|
|
18
20
|
|
|
19
21
|
async function selectCloudVoice(ai, getApiKey) {
|
|
20
|
-
const provider =
|
|
21
|
-
id: 'my-
|
|
22
|
+
const provider = createTwinCloudTTSProvider({
|
|
23
|
+
id: 'my-cloud-voice',
|
|
22
24
|
model: {
|
|
23
25
|
id: 'fal-ai/elevenlabs/tts/multilingual-v2',
|
|
24
26
|
defaultVoice: 'Rachel'
|
|
25
27
|
},
|
|
26
28
|
getApiKey,
|
|
27
|
-
maxConcurrentRequests: 4
|
|
29
|
+
maxConcurrentRequests: 4,
|
|
30
|
+
followUpQueue: {maxConcurrentRequests: 4, intervalMs: 250}
|
|
28
31
|
});
|
|
29
32
|
await ai.configureSpeechProvider('tts', provider, {
|
|
30
33
|
modelId: provider.catalog()[0].id
|
|
@@ -33,6 +36,10 @@ async function selectCloudVoice(ai, getApiKey) {
|
|
|
33
36
|
}
|
|
34
37
|
```
|
|
35
38
|
|
|
39
|
+
`createDigitalOceanFalTTSProvider` remains a compatibility export of the same
|
|
40
|
+
factory. Existing provider IDs and credentials remain valid; the TWiN Cloud
|
|
41
|
+
name does not change the underlying DigitalOcean FAL endpoint or protocol.
|
|
42
|
+
|
|
36
43
|
Construction and configuration make no network request. Explicit
|
|
37
44
|
`ai.setSpeechMuted(false)` awaits the key reader, checks credential presence, and activates
|
|
38
45
|
the adapter; its ready state does not prove remote credential validity.
|
|
@@ -53,11 +60,49 @@ punctuation segmentation, concurrent provider queue, and original-order
|
|
|
53
60
|
playback remain the only owners. Use
|
|
54
61
|
`ai.configureTTSSegmentation({punctuation:'any',wordCadence:null})` when each
|
|
55
62
|
punctuation boundary should submit promptly. This is incremental playback of
|
|
56
|
-
completed synthesis jobs, not a live audio stream from
|
|
57
|
-
|
|
58
|
-
The
|
|
59
|
-
|
|
60
|
-
|
|
63
|
+
completed synthesis jobs, not a live audio stream from TWiN Cloud.
|
|
64
|
+
|
|
65
|
+
The SDK submits `POST https://inference.do-ai.run/v1/async-invoke` immediately
|
|
66
|
+
after credential readiness, outside the follow-up queue. It then reads that job
|
|
67
|
+
until its completed `output.audio.url` is available. The provider's
|
|
68
|
+
`maxConcurrentRequests` retains its whole-synthesis-job meaning; it is separate
|
|
69
|
+
from `followUpQueue.maxConcurrentRequests`.
|
|
70
|
+
|
|
71
|
+
One provider-local `js-queue` owns only follow-up status/result GETs and the final
|
|
72
|
+
audio GET. `followUpQueue` defaults to `{maxConcurrentRequests:4,intervalMs:250}`;
|
|
73
|
+
the application may supply those settings explicitly. A follow-up slot remains
|
|
74
|
+
occupied through the complete JSON, audio Blob, or error response body. Dispatch
|
|
75
|
+
starts observe `intervalMs`, with at most the configured number in flight. Each
|
|
76
|
+
job awaits its current response and body before immediately enqueueing another
|
|
77
|
+
request only when incomplete or eligible for a retry. There is no additional
|
|
78
|
+
per-job polling delay.
|
|
79
|
+
|
|
80
|
+
A readable `Retry-After` changes only the affected job's next eligibility, not
|
|
81
|
+
sibling requests or initial submissions. A readable `429` permits one retry of
|
|
82
|
+
that submission or status read; submission retries retain a one-second minimum, while
|
|
83
|
+
follow-up retries use the queue cadence and any longer readable `Retry-After`.
|
|
84
|
+
An accepted job's status Fetch network failure may retry once for the same job;
|
|
85
|
+
JSON parsing failures are not network retries. Repeated failures surface their
|
|
86
|
+
complete diagnostics without an endless retry loop; a failed job does not cancel
|
|
87
|
+
siblings. Retry diagnostics remain in the developer console, outside conversation
|
|
88
|
+
history.
|
|
89
|
+
|
|
90
|
+
The queue is created when this cloud factory is called, not when the browser
|
|
91
|
+
speech entrypoint is imported. Local Kokoro and Whisper do not use this queue;
|
|
92
|
+
their setup, Workers, synthesis/transcription, and lifecycle remain independent.
|
|
93
|
+
|
|
94
|
+
The SDK pins published `js-queue` through npm. Its browser Queue entry, original
|
|
95
|
+
package metadata, and license are projected unchanged by
|
|
96
|
+
`node tools/sync-js-queue.mjs` after an SDK dependency update. The existing runtime
|
|
97
|
+
materializer serves that same entry; applications do not need another import-map
|
|
98
|
+
setting. `easy-stack` remains an npm dependency and is not imported by the Queue
|
|
99
|
+
entry. Do not hand-edit this upstream projection.
|
|
100
|
+
|
|
101
|
+
An ambiguous submission network failure is not automatically resubmitted: a
|
|
102
|
+
browser cannot distinguish a rejected preflight from an accepted paid job whose
|
|
103
|
+
response was lost. This contract has no submission idempotency mechanism, and
|
|
104
|
+
unreadable response headers cannot supply a retry delay. Follow-up pacing does
|
|
105
|
+
not claim to free upstream concurrency slots or fix CORS.
|
|
61
106
|
The audio download never receives the inference Authorization header.
|
|
62
107
|
The selected multilingual-v2 service supports speed `0.7` through `1.2`;
|
|
63
108
|
unsupported speeds reject rather than being silently clamped. Complete
|
|
@@ -924,8 +969,8 @@ records; this contract does not freeze them or shorten their content.
|
|
|
924
969
|
| --- | --- | --- |
|
|
925
970
|
| Browser | Shipped | Requires Workers, Fetch, Blob/File, object URLs, DBOPFS/OPFS, and Web Locks. Blob/File STT requests also require the browser audio decoder. |
|
|
926
971
|
| Native WebView | Conditional | Available when the WebView exposes the browser APIs above. It does not invoke Core speech. |
|
|
927
|
-
| Node | Remote adapter available; local execution unavailable |
|
|
928
|
-
| Remote TTS | Explicitly selected |
|
|
972
|
+
| Node | Remote adapter available; local execution unavailable | TWiN Cloud uses Fetch and Blob. The local providers have no SDK Node speech-storage, Worker, or audio-decoder host. |
|
|
973
|
+
| Remote TTS | Explicitly selected | TWiN Cloud uses standard Fetch and Blob with an application-owned key; no Worker, OPFS, or local model is needed for this adapter. |
|
|
929
974
|
|
|
930
975
|
STT and TTS own independent provider lifecycles. A failure or cancellation in
|
|
931
976
|
one role does not disable the other role or authorize a fallback provider.
|
|
@@ -941,7 +986,8 @@ import {
|
|
|
941
986
|
createBrowserSpeechAuthority,
|
|
942
987
|
createBrowserWhisperProvider,
|
|
943
988
|
createDbopfsSpeechArtifactStore,
|
|
944
|
-
createDigitalOceanFalTTSProvider
|
|
989
|
+
createDigitalOceanFalTTSProvider,
|
|
990
|
+
createTwinCloudTTSProvider
|
|
945
991
|
} from 'arcane-os/ai/browser-speech';
|
|
946
992
|
```
|
|
947
993
|
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
"minimumVersion": "22.23.2 for Node entrypoints",
|
|
7
7
|
"moduleSystem": "ESM"
|
|
8
8
|
},
|
|
9
|
-
"memberCount":
|
|
9
|
+
"memberCount": 219,
|
|
10
10
|
"runtimeSubpathPatterns": {
|
|
11
11
|
"arcane-os/modules/*": "./runtime/arcane/modules/*",
|
|
12
12
|
"arcane-os/entities/*": "./runtime/arcane/entities/*"
|
|
@@ -3111,14 +3111,28 @@
|
|
|
3111
3111
|
"name": "createDigitalOceanFalTTSProvider",
|
|
3112
3112
|
"displayName": "createDigitalOceanFalTTSProvider()",
|
|
3113
3113
|
"kind": "function",
|
|
3114
|
-
"signature": "createDigitalOceanFalTTSProvider({id,model,getApiKey,maxConcurrentRequests=4,fetch=globalThis.fetch}={})",
|
|
3114
|
+
"signature": "createDigitalOceanFalTTSProvider({id,model,getApiKey,maxConcurrentRequests=4,followUpQueue:{maxConcurrentRequests=4,intervalMs=250}={},fetch=globalThis.fetch}={})",
|
|
3115
3115
|
"entrypoints": ["arcane-os/ai/browser-speech"],
|
|
3116
3116
|
"primaryImport": "arcane-os/ai/browser-speech",
|
|
3117
3117
|
"group": "Browser speech providers",
|
|
3118
|
-
"summary": "
|
|
3118
|
+
"summary": "Compatibility export of createTwinCloudTTSProvider using the same function and implementation.",
|
|
3119
3119
|
"availability": "Browser or Node with Fetch, Blob, AbortController, and an application-owned DigitalOcean inference key",
|
|
3120
3120
|
"protocol": "arcane-ai-provider/2 tts/synthesize",
|
|
3121
|
-
"normalization": "
|
|
3121
|
+
"normalization": "Same TWiN Cloud options, queued follow-ups, complete audio Blob results, and cancellation contract. Existing imports, stored provider IDs, and credentials remain valid without migration."
|
|
3122
|
+
},
|
|
3123
|
+
{
|
|
3124
|
+
"id": "browser-speech:createTwinCloudTTSProvider",
|
|
3125
|
+
"name": "createTwinCloudTTSProvider",
|
|
3126
|
+
"displayName": "createTwinCloudTTSProvider()",
|
|
3127
|
+
"kind": "function",
|
|
3128
|
+
"signature": "createTwinCloudTTSProvider({id,model,getApiKey,maxConcurrentRequests=4,followUpQueue:{maxConcurrentRequests=4,intervalMs=250}={},fetch=globalThis.fetch}={})",
|
|
3129
|
+
"entrypoints": ["arcane-os/ai/browser-speech"],
|
|
3130
|
+
"primaryImport": "arcane-os/ai/browser-speech",
|
|
3131
|
+
"group": "Browser speech providers",
|
|
3132
|
+
"summary": "Creates an explicitly selected TWiN Cloud remote TTS provider with immediate synthesis submission and configurable queued audio follow-ups.",
|
|
3133
|
+
"availability": "Browser or Node with Fetch, Blob, AbortController, and an application-owned inference key",
|
|
3134
|
+
"protocol": "arcane-ai-provider/2 tts/synthesize",
|
|
3135
|
+
"normalization": "Construction and activation make no adapter network requests. SDK initial POSTs bypass the provider-local js-queue; follow-up status/result/audio GETs use followUpQueue capacity and cadence through complete body consumption. Retry-After affects only its job. Whole-job maxConcurrentRequests remains independent. Local Kokoro and Whisper paths are unchanged. Applications own model, voice, credentials, activation, and queue settings."
|
|
3122
3136
|
},
|
|
3123
3137
|
{
|
|
3124
3138
|
"id": "browser-speech:createBrowserKokoroProvider",
|
package/docs/reference/pwa.md
CHANGED
|
@@ -93,6 +93,16 @@ The application entry and generated PWA shell records are retained. Select the
|
|
|
93
93
|
resources needed by every offline page, including its shared modules and styles.
|
|
94
94
|
The worker script itself is never an application cache entry.
|
|
95
95
|
|
|
96
|
+
For direct installed-package root applications, public `arcane import-map`
|
|
97
|
+
generation follows the same source resource graph as `arcane dev` before writing
|
|
98
|
+
the static offline inventory. Imports from unvisited pages, shared runtime
|
|
99
|
+
modules, component styles, and statically declared Worker URLs retain their
|
|
100
|
+
authored query values. SDK component `./arcane/` resource attributes resolve from
|
|
101
|
+
that component's actual runtime root, including installed package aliases and
|
|
102
|
+
nested component directories; ordinary application document bases keep their
|
|
103
|
+
normal meaning. The graph selects resources already present in the configured
|
|
104
|
+
routes. It does not invent resource paths or enumerate user-specific page URLs.
|
|
105
|
+
|
|
96
106
|
These settings describe published application resources. They do not select
|
|
97
107
|
user uploads, provider responses, live API requests, saved conversations,
|
|
98
108
|
preferences, IndexedDB, OPFS, or a model owner's download/cache lifecycle.
|
|
@@ -180,7 +190,7 @@ without loading certificates; see [explicit HTTP development](cli.md#explicit-ht
|
|
|
180
190
|
`--public` selects the IPv4 wildcard bind address; it does not enable PWA
|
|
181
191
|
configuration, change manifest metadata, or determine browser installability.
|
|
182
192
|
|
|
183
|
-
|
|
193
|
+
Live source inventory work begins when the browser requests the worker or current
|
|
184
194
|
offline manifest, after the page can start. It traverses the selected route
|
|
185
195
|
inventory once for that request, follows page and runtime resource references
|
|
186
196
|
to retain selected query variants, and shares an in-flight traversal with
|
|
@@ -189,6 +199,11 @@ document corpus bodies remain under their existing owner. It does not rebuild
|
|
|
189
199
|
the application. Installation metadata and bootstrap requests reuse the current
|
|
190
200
|
generated bundle.
|
|
191
201
|
|
|
202
|
+
An explicit root import-map refresh also performs that traversal to produce the
|
|
203
|
+
committed static PWA files. It opens no server or listener. Its generated
|
|
204
|
+
inventory is available to ordinary static hosting without first visiting each
|
|
205
|
+
page through the development server.
|
|
206
|
+
|
|
192
207
|
The SDK owns version information in `arcane-offline.json`:
|
|
193
208
|
|
|
194
209
|
| Field | Owner and update rule |
|
|
@@ -1566,8 +1566,10 @@ boundary applies to STT activation, transcription, TTS lifecycle, synthesis,
|
|
|
1566
1566
|
and playback failures.
|
|
1567
1567
|
|
|
1568
1568
|
`reportTTSError()` recognizes `synthesis`, `decode`, `playback-start`, and
|
|
1569
|
-
`playback-resume`. It
|
|
1570
|
-
|
|
1569
|
+
`playback-resume`. It preserves the user's mute choice, sticky provider
|
|
1570
|
+
readiness, and accepted sibling speech jobs. The AI runtime owns settlement of
|
|
1571
|
+
the failed job; reporting an error neither stops the queue nor retries it. The
|
|
1572
|
+
component emits `speech-synthesis-error` with the exact boundary,
|
|
1571
1573
|
stable reason/code, generic user-safe status, and complete Error in the local
|
|
1572
1574
|
event detail.
|
|
1573
1575
|
|
|
@@ -1600,8 +1602,16 @@ same capture operation id rather than inventing a second correlation boundary.
|
|
|
1600
1602
|
User Unmute calls the shared `AI.setSpeechMuted(false)` lifecycle owner before
|
|
1601
1603
|
or with publishing the TTS load intent, so the runtime can legally load TTS.
|
|
1602
1604
|
Configured `initialMuted:false` records that same unmute intent even when the
|
|
1603
|
-
TTS route is still unselected or loading
|
|
1604
|
-
|
|
1605
|
+
TTS route is still unselected or loading. Public `muted` expresses the user's
|
|
1606
|
+
choice; role availability and the AI runtime still prevent playback until the
|
|
1607
|
+
selected role reaches `ready`, when the waiting intent is applied. A failed
|
|
1608
|
+
load ends its activation attempt without changing that choice or starting
|
|
1609
|
+
another attempt. Retry voice explicitly starts a new attempt, including when
|
|
1610
|
+
the preserved choice is unmuted; an unloaded selected voice offers Load voice.
|
|
1611
|
+
When a role returns to ready, permitted queued playback resumes through the
|
|
1612
|
+
existing AI owner without repeating activation or replaying failed jobs.
|
|
1613
|
+
Synthesis, decode, and playback failures also
|
|
1614
|
+
preserve that choice and do not automatically replay failed speech.
|
|
1605
1615
|
Mute calls `AI.setSpeechMuted(true)`, stops playback, cancels active TTS work,
|
|
1606
1616
|
and unloads the selected TTS role. Lifecycle failures remain visible through
|
|
1607
1617
|
`speech-tts-lifecycle-error` and sticky role state.
|
|
@@ -67,7 +67,7 @@ runtime layouts.
|
|
|
67
67
|
| `arcane-os/ai/browser-wasm` | Caller-selected browser-local Wllama inference, complete DBOPFS model storage, streaming, cancellation, and structural tool-call results. |
|
|
68
68
|
| `arcane-os/ai/tool-text-stream` | Shared selected tool-argument text observer for provider integration. |
|
|
69
69
|
| `arcane-os/ai/twin-cloud` | Complete TWiN Cloud requests from Node or a browser with an explicit key/model and shared retry/cancellation behavior. |
|
|
70
|
-
| `arcane-os/ai/browser-speech` | Caller-selected browser-local Whisper STT and Kokoro TTS, explicit
|
|
70
|
+
| `arcane-os/ai/browser-speech` | Caller-selected browser-local Whisper STT and Kokoro TTS, explicit TWiN Cloud remote TTS, ordinary upstream assets, role lifecycle, and cancellation. |
|
|
71
71
|
| `arcane-os/mail` | Portable Mail runtime, durable outbox, complete transport responses, and provider-neutral acceptance contracts. |
|
|
72
72
|
|
|
73
73
|
These lowercase runtime-module entrypoints expose their existing exports; they
|
|
@@ -178,7 +178,8 @@ browser map are cataloged separately in [Runtime modules](runtime-modules.md).
|
|
|
178
178
|
| `getPwaInstall()` | function | `arcane-os/pwa` | Progressive web applications | Browser native installation events; waiting state while no prompt is available |
|
|
179
179
|
| `mountPwaInstallPrompt()` | function | `arcane-os/pwa` | Progressive web applications | Browser document and managed HTML import; resolves to null without a document |
|
|
180
180
|
| `createDbopfsSpeechArtifactStore()` | function | `arcane-os/ai/browser-speech` | Browser speech providers | Browser with ready DBOPFS, Web Locks, Fetch, File/Blob, and object URLs |
|
|
181
|
-
| `createDigitalOceanFalTTSProvider()` | function | `arcane-os/ai/browser-speech` | Browser speech providers |
|
|
181
|
+
| `createDigitalOceanFalTTSProvider()` | function | `arcane-os/ai/browser-speech` | Browser speech providers | Compatibility name for `createTwinCloudTTSProvider()` |
|
|
182
|
+
| `createTwinCloudTTSProvider()` | function | `arcane-os/ai/browser-speech` | Browser speech providers | Browser or Node with Fetch, Blob, and an application-supplied inference key |
|
|
182
183
|
| `removeBrowserSpeechModelCache()` | function | `arcane-os/ai/browser-speech` | Browser speech providers | Browser CacheStorage; explicit removal of one selected upstream model |
|
|
183
184
|
| `createNativeBuildPlan()` | function | `arcane-os` | Targets, native plans, and providers | Node; selected browser/native target or provider as documented |
|
|
184
185
|
| `createNativeTargetAdapter()` | function | `arcane-os` | Targets, native plans, and providers | Node; selected browser/native target or provider as documented |
|
|
@@ -7184,36 +7185,41 @@ async function transcribeAfterUserChoice(audioBlob) {
|
|
|
7184
7185
|
}
|
|
7185
7186
|
```
|
|
7186
7187
|
|
|
7187
|
-
##
|
|
7188
|
+
## createTwinCloudTTSProvider()
|
|
7188
7189
|
|
|
7189
7190
|
### Overview
|
|
7190
7191
|
|
|
7191
|
-
Creates a `localOnly:false` provider/2 TTS adapter for the caller-selected
|
|
7192
|
-
|
|
7193
|
-
|
|
7192
|
+
Creates a `localOnly:false` TWiN Cloud provider/2 TTS adapter for the caller-selected
|
|
7193
|
+
model. It submits independent synthesis jobs, polls their completion, and returns
|
|
7194
|
+
each complete downloaded audio `Blob`. The shared AI
|
|
7194
7195
|
queue owns punctuation segmentation, bounded concurrent requests, and playback
|
|
7195
7196
|
in input order; this is not provider-native live audio streaming.
|
|
7196
7197
|
|
|
7197
|
-
### Signature and
|
|
7198
|
+
### Signature and result
|
|
7198
7199
|
|
|
7199
7200
|
```text
|
|
7200
|
-
|
|
7201
|
+
createTwinCloudTTSProvider({id,model,getApiKey,maxConcurrentRequests=4,followUpQueue:{maxConcurrentRequests=4,intervalMs=250}={},fetch=globalThis.fetch}={})
|
|
7201
7202
|
```
|
|
7202
7203
|
|
|
7203
7204
|
`id` is the application-owned provider identifier. `model` contains the selected
|
|
7204
7205
|
`id` and `defaultVoice`; the supported multilingual-v2 selection is
|
|
7205
7206
|
`fal-ai/elevenlabs/tts/multilingual-v2`. `getApiKey()` returns the
|
|
7206
7207
|
application's current DigitalOcean inference key or a promise for it. The credential is never
|
|
7207
|
-
included in catalog or status. `maxConcurrentRequests`
|
|
7208
|
-
|
|
7209
|
-
|
|
7208
|
+
included in catalog or status. `maxConcurrentRequests` remains the positive
|
|
7209
|
+
whole-synthesis-job capacity used by the shared AI runtime. It is independent
|
|
7210
|
+
from `followUpQueue.maxConcurrentRequests`, which controls follow-up HTTP
|
|
7211
|
+
operations. `followUpQueue.intervalMs` controls the spacing between those
|
|
7212
|
+
dispatch starts. Optional `fetch` supplies the standard Fetch interface.
|
|
7210
7213
|
|
|
7211
7214
|
### Availability and normalization
|
|
7212
7215
|
|
|
7213
7216
|
The adapter needs Fetch, Blob, and AbortController; it needs no Worker or
|
|
7214
7217
|
DBOPFS. Browser access depends on endpoint CORS support. Construction makes
|
|
7215
|
-
no network request.
|
|
7216
|
-
|
|
7218
|
+
no network request. Its provider-local `js-queue` is created only when this
|
|
7219
|
+
factory is called; importing the entrypoint does not create a queue or load a
|
|
7220
|
+
model. Local Kokoro and Whisper retain their independent paths. Inspection and
|
|
7221
|
+
activation await the application's key reader without making an SDK network
|
|
7222
|
+
request. Activation checks
|
|
7217
7223
|
local credential presence, not remote validity. Status reports remote
|
|
7218
7224
|
execution and current activity. Requests preserve complete text, selected
|
|
7219
7225
|
voice, and speed; multilingual-v2 speed outside `0.7` through `1.2` rejects
|
|
@@ -7221,6 +7227,22 @@ instead of clamping. Returned media is fetched without the inference key.
|
|
|
7221
7227
|
Cancelling a credential wait leaves the application's shared refresh alone
|
|
7222
7228
|
and prevents a late result from starting this cancelled operation.
|
|
7223
7229
|
|
|
7230
|
+
The SDK sends each initial synthesis POST immediately after credential readiness,
|
|
7231
|
+
outside the follow-up queue. One queue handles only status/result GETs and the
|
|
7232
|
+
final audio download. Its defaults are four in-flight follow-ups and 250 ms
|
|
7233
|
+
between dispatch starts, with each slot held through complete JSON, Blob, or
|
|
7234
|
+
error-body consumption. Each job awaits its response and body before enqueueing
|
|
7235
|
+
its next incomplete-status check; there is no per-job polling delay.
|
|
7236
|
+
|
|
7237
|
+
A readable `Retry-After` postpones only the affected job's next eligibility.
|
|
7238
|
+
Other jobs and initial submissions remain independent. One readable HTTP 429
|
|
7239
|
+
retry is allowed per submission or status read; POST retries retain a one-second minimum, and
|
|
7240
|
+
follow-up retries use the configured queue cadence plus any longer readable
|
|
7241
|
+
`Retry-After`. An accepted job's failed status Fetch may retry once after a
|
|
7242
|
+
network failure, not after a JSON parsing failure. Repeated failures surface
|
|
7243
|
+
complete developer diagnostics without cancelling sibling jobs. An ambiguous
|
|
7244
|
+
paid POST network failure is never automatically replayed.
|
|
7245
|
+
|
|
7224
7246
|
Each provider/2 `tts/synthesize` request returns one complete audio Blob.
|
|
7225
7247
|
Request cancellation, unload, and disposal stop local polling/downloads and
|
|
7226
7248
|
discard late results; they cannot promise cancellation of an already submitted
|
|
@@ -7230,13 +7252,14 @@ does not select a fallback or silently resubmit a paid synthesis POST.
|
|
|
7230
7252
|
### Example
|
|
7231
7253
|
|
|
7232
7254
|
```javascript
|
|
7233
|
-
import {
|
|
7255
|
+
import {createTwinCloudTTSProvider} from 'arcane-os/ai/browser-speech';
|
|
7234
7256
|
|
|
7235
7257
|
async function configureCloudVoice(ai, getApiKey) {
|
|
7236
|
-
const provider =
|
|
7258
|
+
const provider = createTwinCloudTTSProvider({
|
|
7237
7259
|
id: 'selected-cloud-voice',
|
|
7238
7260
|
model: {id: 'fal-ai/elevenlabs/tts/multilingual-v2', defaultVoice: 'Rachel'},
|
|
7239
|
-
getApiKey
|
|
7261
|
+
getApiKey,
|
|
7262
|
+
followUpQueue: {maxConcurrentRequests: 4, intervalMs: 250}
|
|
7240
7263
|
});
|
|
7241
7264
|
await ai.configureSpeechProvider('tts', provider);
|
|
7242
7265
|
return provider;
|
|
@@ -7245,7 +7268,42 @@ async function configureCloudVoice(ai, getApiKey) {
|
|
|
7245
7268
|
|
|
7246
7269
|
Call activation and synthesis from the application's explicit speech action.
|
|
7247
7270
|
For streaming text, cancellation, and independent STT configuration, see
|
|
7248
|
-
[
|
|
7271
|
+
[TWiN Cloud speech](ai/browser-speech.md#twin-cloud-text-to-speech).
|
|
7272
|
+
|
|
7273
|
+
## createDigitalOceanFalTTSProvider()
|
|
7274
|
+
|
|
7275
|
+
### Overview
|
|
7276
|
+
|
|
7277
|
+
Compatibility export of `createTwinCloudTTSProvider`, using the same function
|
|
7278
|
+
and implementation. The canonical public name is TWiN Cloud; the underlying
|
|
7279
|
+
DigitalOcean FAL endpoint and job protocol remain unchanged.
|
|
7280
|
+
|
|
7281
|
+
### Signature and result
|
|
7282
|
+
|
|
7283
|
+
```text
|
|
7284
|
+
createDigitalOceanFalTTSProvider({id,model,getApiKey,maxConcurrentRequests=4,followUpQueue:{maxConcurrentRequests=4,intervalMs=250}={},fetch=globalThis.fetch}={})
|
|
7285
|
+
```
|
|
7286
|
+
|
|
7287
|
+
Accepts the same options and returns the same provider as
|
|
7288
|
+
[`createTwinCloudTTSProvider()`](#createtwincloudttsprovider).
|
|
7289
|
+
|
|
7290
|
+
### Availability and normalization
|
|
7291
|
+
|
|
7292
|
+
The alias shares the canonical factory's Browser and Node requirements,
|
|
7293
|
+
immediate submission, queued follow-ups, cancellation, and result contract.
|
|
7294
|
+
Existing caller imports, stored provider IDs, and credentials remain valid;
|
|
7295
|
+
renaming a caller's import requires no saved-data migration.
|
|
7296
|
+
|
|
7297
|
+
### Example
|
|
7298
|
+
|
|
7299
|
+
```javascript
|
|
7300
|
+
import {
|
|
7301
|
+
createTwinCloudTTSProvider,
|
|
7302
|
+
createDigitalOceanFalTTSProvider
|
|
7303
|
+
} from 'arcane-os/ai/browser-speech';
|
|
7304
|
+
|
|
7305
|
+
console.log(createDigitalOceanFalTTSProvider === createTwinCloudTTSProvider);
|
|
7306
|
+
```
|
|
7249
7307
|
|
|
7250
7308
|
## createBrowserKokoroProvider()
|
|
7251
7309
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "arcane-os",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.41.0",
|
|
4
4
|
"description": "Arcane OS JavaScript SDK, project-local CLI, browser runtime, and repository-portable application packager.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./src/index.mjs",
|
|
@@ -124,6 +124,7 @@
|
|
|
124
124
|
},
|
|
125
125
|
"dependencies": {
|
|
126
126
|
"event-pubsub": "6.1.0",
|
|
127
|
+
"js-queue": "3.1.1",
|
|
127
128
|
"node-http-server": "10.0.0",
|
|
128
129
|
"strong-type": "2.0.1",
|
|
129
130
|
"vanilla-test": "2.1.3"
|