arcane-os 0.5.17 → 0.5.18
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 +24 -0
- package/README.md +9 -9
- package/bin/arcane-test.mjs +16 -13
- package/browser-runtime/ai/browser-speech-artifacts.mjs +41 -38
- package/browser-runtime/ai/browser-speech-providers.mjs +46 -43
- package/browser-runtime/ai/browser-wasm-llm-provider.mjs +127 -124
- package/browser-runtime/ai/browser-wasm.mjs +6 -3
- package/browser-runtime/ai/browser-wllama-runtime.mjs +26 -23
- package/browser-runtime/ai/model-controller.mjs +94 -91
- package/browser-runtime/ai/speech-worker-client.mjs +12 -9
- package/browser-runtime/ai/speech-worker-runtime.mjs +56 -53
- package/browser-runtime/dependencies/strong-type/index.js +207 -82
- package/browser-runtime/dependencies/strong-type/package.json +15 -6
- package/browser-runtime/dom-event-instrumentation.mjs +24 -20
- package/browser-runtime/event-manager.mjs +111 -108
- package/browser-runtime/speech-text.mjs +5 -1
- package/docs/reference/ai/browser-speech.md +198 -5
- package/docs/reference/availability-and-normalization.md +40 -0
- package/docs/reference/inventory/package-api.json +1 -1
- package/docs/reference/inventory/runtime-components.json +1 -1
- package/docs/reference/inventory/runtime-modules.json +20 -24
- package/docs/reference/protocols.md +1 -1
- package/docs/reference/runtime-modules.md +125 -35
- package/docs/reference/sdk-api.md +23 -7
- package/examples/wasm-ai-demo/index.html +1 -0
- package/node_modules/event-pubsub/node_modules/strong-type/README.md +408 -0
- package/node_modules/event-pubsub/node_modules/strong-type/assets/strong-type-header.png +0 -0
- package/node_modules/event-pubsub/node_modules/strong-type/index.js +1151 -0
- package/node_modules/event-pubsub/node_modules/strong-type/licence +21 -0
- package/node_modules/event-pubsub/node_modules/strong-type/node.js +125 -0
- package/node_modules/event-pubsub/node_modules/strong-type/package.json +61 -0
- package/node_modules/strong-type/README.md +42 -24
- package/node_modules/strong-type/benchmark/core.js +324 -0
- package/node_modules/strong-type/index.js +207 -82
- package/node_modules/strong-type/package.json +14 -6
- package/package.json +3 -3
- package/runtime/arcane/components/app-bar.html +5 -2
- package/runtime/arcane/components/assistant-panel.html +5 -2
- package/runtime/arcane/components/chart.html +15 -12
- package/runtime/arcane/components/chat.html +117 -138
- package/runtime/arcane/components/dashboard-config.html +6 -3
- package/runtime/arcane/components/data-view.html +4 -1
- package/runtime/arcane/components/directory-picker.html +5 -2
- package/runtime/arcane/components/document-inspector.html +6 -3
- package/runtime/arcane/components/file-drop.html +5 -2
- package/runtime/arcane/components/file-inspector.html +9 -6
- package/runtime/arcane/components/file-manager.html +16 -13
- package/runtime/arcane/components/local-ai-status.html +5 -2
- package/runtime/arcane/components/markdown-document.html +14 -11
- package/runtime/arcane/components/markdown-editor.html +4 -1
- package/runtime/arcane/components/modal.html +4 -1
- package/runtime/arcane/components/output-panel.html +6 -3
- package/runtime/arcane/components/preferences-form.html +4 -1
- package/runtime/arcane/components/record-timeline.html +8 -5
- package/runtime/arcane/components/relationship-board.html +9 -6
- package/runtime/arcane/components/source-code-viewer.html +12 -9
- package/runtime/arcane/components/source-explanation.html +8 -5
- package/runtime/arcane/components/speech.html +24 -21
- package/runtime/arcane/components/summary-strip.html +4 -1
- package/runtime/arcane/components/task-progress.html +6 -3
- package/runtime/arcane/components/terminal-workspace.html +4 -1
- package/runtime/arcane/components/voice-transcription.html +11 -8
- package/runtime/arcane/components/web-navigator.html +5 -2
- package/runtime/arcane/entities/ApiModelRecord.js +5 -2
- package/runtime/arcane/entities/Calculation.js +5 -2
- package/runtime/arcane/entities/Chat.js +33 -33
- package/runtime/arcane/entities/CommunicationMessage.js +4 -1
- package/runtime/arcane/entities/CommunicationThread.js +4 -1
- package/runtime/arcane/entities/File.js +1 -1
- package/runtime/arcane/entities/Image.js +8 -5
- package/runtime/arcane/entities/IntentEnvelope.js +20 -17
- package/runtime/arcane/entities/Preference.js +9 -6
- package/runtime/arcane/entities/Theme.js +5 -2
- package/runtime/arcane/entities/User.js +11 -11
- package/runtime/arcane/entities/Weather.js +5 -2
- package/runtime/arcane/modules/AI.js +491 -165
- package/runtime/arcane/modules/AIPreferenceRuntime.js +5 -2
- package/runtime/arcane/modules/AIPreferenceTuple.js +9 -6
- package/runtime/arcane/modules/AIProviderRuntime.js +88 -85
- package/runtime/arcane/modules/AIResponseURLPolicy.js +148 -62
- package/runtime/arcane/modules/AIRuntimeState.js +29 -26
- package/runtime/arcane/modules/AnsiText.js +4 -1
- package/runtime/arcane/modules/ApiModelDatabase.js +22 -19
- package/runtime/arcane/modules/AppDataScope.js +8 -5
- package/runtime/arcane/modules/ArcaneCommunicationBridge.js +4 -1
- package/runtime/arcane/modules/ArcaneNavigationPolicy.js +8 -5
- package/runtime/arcane/modules/ArcaneNetworkPolicy.js +18 -15
- package/runtime/arcane/modules/AsyncBoundary.js +13 -10
- package/runtime/arcane/modules/BrowserTestSuite.js +19 -16
- package/runtime/arcane/modules/CalculatorEngine.js +5 -2
- package/runtime/arcane/modules/ChatRecords.js +18 -15
- package/runtime/arcane/modules/CommunicationAppController.js +9 -6
- package/runtime/arcane/modules/CommunicationHub.js +9 -6
- package/runtime/arcane/modules/CommunicationPreferences.js +4 -1
- package/runtime/arcane/modules/CommunicationProviderRegistry.js +4 -1
- package/runtime/arcane/modules/ComponentContracts.js +56 -53
- package/runtime/arcane/modules/ConfiguredAIChatSession.js +30 -27
- package/runtime/arcane/modules/ConversationActionItems.js +15 -12
- package/runtime/arcane/modules/ConversationClosingReport.js +11 -8
- package/runtime/arcane/modules/ConversationTimebox.js +28 -25
- package/runtime/arcane/modules/CoreLocalModelCatalog.js +17 -14
- package/runtime/arcane/modules/DBLS.js +7 -4
- package/runtime/arcane/modules/DBOPFS.js +7 -7
- package/runtime/arcane/modules/DBOPFSDocumentLibrary.js +34 -31
- package/runtime/arcane/modules/DevelopmentWorkspace.js +4 -1
- package/runtime/arcane/modules/DirectoryPicker.js +8 -5
- package/runtime/arcane/modules/DocumentLexicalSearch.js +11 -8
- package/runtime/arcane/modules/DocumentNavigation.js +7 -4
- package/runtime/arcane/modules/Errors.js +28 -25
- package/runtime/arcane/modules/HTMLImport.js +7 -4
- package/runtime/arcane/modules/IsolatedModelQuestionRunner.js +15 -12
- package/runtime/arcane/modules/LocalAIReadiness.js +21 -18
- package/runtime/arcane/modules/LocalAIReadinessController.js +6 -3
- package/runtime/arcane/modules/MD.js +1 -1
- package/runtime/arcane/modules/Mail.js +46 -43
- package/runtime/arcane/modules/MailOutbox.mjs +48 -45
- package/runtime/arcane/modules/MailTransport.mjs +29 -26
- package/runtime/arcane/modules/MemoryRecords.js +7 -4
- package/runtime/arcane/modules/MessageAdvisory.js +6 -3
- package/runtime/arcane/modules/ModelDefinition.js +7 -4
- package/runtime/arcane/modules/Ollama.js +6 -3
- package/runtime/arcane/modules/OllamaModelIdentifier.js +4 -1
- package/runtime/arcane/modules/OpenMeteoWeatherProvider.js +18 -15
- package/runtime/arcane/modules/PersistentAIChatSession.js +33 -30
- package/runtime/arcane/modules/PreferenceStore.js +20 -17
- package/runtime/arcane/modules/PreparedSpeech.js +485 -0
- package/runtime/arcane/modules/Questionnaire.js +6 -3
- package/runtime/arcane/modules/RecordLinkIndex.js +4 -1
- package/runtime/arcane/modules/RecordPassageIndex.js +9 -6
- package/runtime/arcane/modules/RecordReviewStore.js +12 -9
- package/runtime/arcane/modules/RiskSignalAnalyzer.js +4 -1
- package/runtime/arcane/modules/ScopedOPFSCache.js +6 -3
- package/runtime/arcane/modules/ScreenCapture.js +20 -17
- package/runtime/arcane/modules/SpeechPlayback.js +29 -26
- package/runtime/arcane/modules/StaticDocumentCatalog.js +33 -30
- package/runtime/arcane/modules/SystemAppearance.js +5 -2
- package/runtime/arcane/modules/SystemToolRegistry.js +6 -3
- package/runtime/arcane/modules/TerminalClient.js +14 -11
- package/runtime/arcane/modules/TerminalCommandRegistry.js +4 -1
- package/runtime/arcane/modules/ThemeBootstrap.js +9 -6
- package/runtime/arcane/modules/ThemeManager.js +5 -2
- package/runtime/arcane/modules/TimeGuard.js +1 -1
- package/runtime/arcane/modules/ToolCallRouter.js +11 -8
- package/runtime/arcane/modules/WaitForComponent.js +19 -16
- package/runtime/arcane/modules/YouTubeMedia.js +4 -1
- package/runtime/strong-type/index.js +1276 -352
- package/runtime/strong-type/package.json +69 -45
- package/src/app-descriptor.mjs +17 -14
- package/src/application-tests.mjs +4 -1
- package/src/cli/main.mjs +8 -5
- package/src/constants.mjs +4 -1
- package/src/dev-server.mjs +8 -5
- package/src/doctor.mjs +5 -2
- package/src/dom-event-instrumentation.mjs +24 -20
- package/src/errors.mjs +7 -3
- package/src/event-manager.mjs +111 -108
- package/src/event-queue.mjs +8 -5
- package/src/events.mjs +12 -9
- package/src/import-map.mjs +35 -27
- package/src/installed-sdk-runtime.mjs +4 -1
- package/src/integrated-provider-loader.mjs +9 -6
- package/src/mail-credentials.mjs +16 -13
- package/src/mail-server.mjs +53 -50
- package/src/mail.mjs +18 -15
- package/src/native-plan.mjs +12 -9
- package/src/native-provider-loader.mjs +7 -4
- package/src/packager/core.mjs +18 -15
- package/src/process.mjs +6 -3
- package/src/release-bundle.mjs +14 -11
- package/src/runtime.mjs +4 -1
- package/src/scaffold.mjs +9 -6
- package/src/sdk-browser-runtime.mjs +4 -1
- package/src/source-server.mjs +17 -14
- package/src/targets/index.mjs +5 -2
- package/src/templates/workspace-template.mjs +13 -6
- package/src/testing-loader.mjs +7 -4
- package/src/testing.mjs +10 -7
- package/src/toolchain.mjs +13 -10
- package/src/update-check.mjs +9 -6
- package/src/workspace-operation-lock.mjs +14 -11
- package/src/workspace-runtime.mjs +4 -1
- package/src/workspace.mjs +17 -14
- package/runtime/arcane/modules/AIResponseLength.js +0 -32
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
import Is from 'strong-type';
|
|
2
|
+
const is=new Is(false);
|
|
3
|
+
|
|
1
4
|
import { arcaneLogging } from 'arcane-os/logging';
|
|
2
5
|
import {createArcaneEventSource} from 'arcane-os/event-manager';
|
|
3
6
|
import GifEncoder from './GifEncoder.js';
|
|
@@ -163,10 +166,10 @@ function signalLike(value){
|
|
|
163
166
|
return value===undefined
|
|
164
167
|
||value===null
|
|
165
168
|
||(
|
|
166
|
-
|
|
167
|
-
&&
|
|
168
|
-
&&
|
|
169
|
-
&&
|
|
169
|
+
is.object(value)
|
|
170
|
+
&&is.boolean(value.aborted)
|
|
171
|
+
&&is.function(value.addEventListener)
|
|
172
|
+
&&is.function(value.removeEventListener)
|
|
170
173
|
);
|
|
171
174
|
}
|
|
172
175
|
|
|
@@ -190,7 +193,7 @@ function abortError(cause){
|
|
|
190
193
|
|
|
191
194
|
function optionsRecord(value,label){
|
|
192
195
|
if(value===undefined)return {};
|
|
193
|
-
if(!value
|
|
196
|
+
if(!value||!is.object(value)||is.array(value)){
|
|
194
197
|
throw captureError(
|
|
195
198
|
SCREEN_CAPTURE_ERRORS.optionsRecordRejected,
|
|
196
199
|
label+' must be an object.',
|
|
@@ -211,7 +214,7 @@ function optionsRecord(value,label){
|
|
|
211
214
|
|
|
212
215
|
function admittedOperationId(value){
|
|
213
216
|
if(value===undefined||value===null)return null;
|
|
214
|
-
if(
|
|
217
|
+
if(!is.string(value)
|
|
215
218
|
||value.trim()!==value
|
|
216
219
|
||!value){
|
|
217
220
|
throw captureError(
|
|
@@ -225,7 +228,7 @@ function admittedOperationId(value){
|
|
|
225
228
|
}
|
|
226
229
|
|
|
227
230
|
function imageResultType(blob){
|
|
228
|
-
const reported=
|
|
231
|
+
const reported=is.string(blob?.type)?blob.type.trim().toLowerCase():'';
|
|
229
232
|
if(!reported)return SCREEN_CAPTURE_IMAGE_TYPE_FALLBACK;
|
|
230
233
|
const subtype=reported.startsWith('image/')
|
|
231
234
|
?reported.slice('image/'.length).split(';',1)[0]
|
|
@@ -252,7 +255,7 @@ function supportedRecorderType(Recorder){
|
|
|
252
255
|
}
|
|
253
256
|
|
|
254
257
|
function reportDetachedError(error){
|
|
255
|
-
if(
|
|
258
|
+
if(is.function(globalThis.reportError))globalThis.reportError(error);
|
|
256
259
|
else arcaneLogging.error?.(error);
|
|
257
260
|
}
|
|
258
261
|
|
|
@@ -436,7 +439,7 @@ export default class ScreenCapture extends EventTarget{
|
|
|
436
439
|
if(this.#events.disposed)return null;
|
|
437
440
|
const compatibilityDetail={
|
|
438
441
|
...detail,
|
|
439
|
-
mode:
|
|
442
|
+
mode:is.string(detail?.mode)?detail.mode:operation.mode,
|
|
440
443
|
operationId:operation.id,
|
|
441
444
|
reason,
|
|
442
445
|
...(code?{code}:{})
|
|
@@ -527,7 +530,7 @@ export default class ScreenCapture extends EventTarget{
|
|
|
527
530
|
|
|
528
531
|
#attachTrackEnd(operation,stream){
|
|
529
532
|
for(const track of stream?.getTracks?.()||[]){
|
|
530
|
-
if(
|
|
533
|
+
if(!is.function(track?.addEventListener))continue;
|
|
531
534
|
const owner=this;
|
|
532
535
|
function stopEndedScreenCaptureTrack(){
|
|
533
536
|
if(!owner.#isCurrent(operation))return;
|
|
@@ -782,7 +785,7 @@ export default class ScreenCapture extends EventTarget{
|
|
|
782
785
|
);
|
|
783
786
|
this.#assertCurrent(operation);
|
|
784
787
|
stage='image-encoding';
|
|
785
|
-
const type=
|
|
788
|
+
const type=is.string(options.type)&&options.type.trim()
|
|
786
789
|
?options.type.trim()
|
|
787
790
|
:'image/png';
|
|
788
791
|
const blob=await this.#canvasBlob(
|
|
@@ -990,7 +993,7 @@ export default class ScreenCapture extends EventTarget{
|
|
|
990
993
|
|
|
991
994
|
#startGifFrameSampling(operation,context,frameDelay){
|
|
992
995
|
const requestedDelay=Number(frameDelay);
|
|
993
|
-
const explicitDelay=
|
|
996
|
+
const explicitDelay=is.finite(requestedDelay)&&requestedDelay>0
|
|
994
997
|
?requestedDelay
|
|
995
998
|
:null;
|
|
996
999
|
let active=true;
|
|
@@ -1000,10 +1003,10 @@ export default class ScreenCapture extends EventTarget{
|
|
|
1000
1003
|
if(explicitDelay!==null){
|
|
1001
1004
|
requestFrame=callback=>setTimeout(()=>callback(monotonicNow()),explicitDelay);
|
|
1002
1005
|
cancelFrame=handle=>clearTimeout(handle);
|
|
1003
|
-
}else if(
|
|
1006
|
+
}else if(is.function(operation.video?.requestVideoFrameCallback)){
|
|
1004
1007
|
requestFrame=callback=>operation.video.requestVideoFrameCallback(callback);
|
|
1005
1008
|
cancelFrame=handle=>operation.video.cancelVideoFrameCallback?.(handle);
|
|
1006
|
-
}else if(
|
|
1009
|
+
}else if(is.function(globalThis.requestAnimationFrame)){
|
|
1007
1010
|
requestFrame=callback=>globalThis.requestAnimationFrame(callback);
|
|
1008
1011
|
cancelFrame=handle=>globalThis.cancelAnimationFrame?.(handle);
|
|
1009
1012
|
}else{
|
|
@@ -1065,7 +1068,7 @@ export default class ScreenCapture extends EventTarget{
|
|
|
1065
1068
|
operation.frames=[];
|
|
1066
1069
|
operation.frameTimes=[];
|
|
1067
1070
|
const requestedDelay=Number(options.frameDelay);
|
|
1068
|
-
operation.gifDelay=
|
|
1071
|
+
operation.gifDelay=is.finite(requestedDelay)&&requestedDelay>0
|
|
1069
1072
|
?requestedDelay
|
|
1070
1073
|
:0;
|
|
1071
1074
|
operation.startedAt=Date.now();
|
|
@@ -1261,7 +1264,7 @@ export default class ScreenCapture extends EventTarget{
|
|
|
1261
1264
|
if(options.signal?.aborted)throw abortError(options.signal.reason);
|
|
1262
1265
|
const width=Number(video.videoWidth);
|
|
1263
1266
|
const height=Number(video.videoHeight);
|
|
1264
|
-
if(!
|
|
1267
|
+
if(!is.finite(width)||width<=0||!is.finite(height)||height<=0){
|
|
1265
1268
|
throw captureError(
|
|
1266
1269
|
SCREEN_CAPTURE_ERRORS.displayMetadataRejected,
|
|
1267
1270
|
'The selected display reported invalid frame dimensions.'
|
|
@@ -1370,7 +1373,7 @@ export default class ScreenCapture extends EventTarget{
|
|
|
1370
1373
|
TypeError
|
|
1371
1374
|
);
|
|
1372
1375
|
}
|
|
1373
|
-
const mode=
|
|
1376
|
+
const mode=is.string(detail?.mode)
|
|
1374
1377
|
?detail.mode
|
|
1375
1378
|
:(this.#operation?.mode??'capture');
|
|
1376
1379
|
const operation=this.#operation??{
|
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
import Is from 'strong-type';
|
|
2
|
+
const is=new Is(false);
|
|
3
|
+
|
|
1
4
|
import {createArcaneEventSource} from 'arcane-os/event-manager';
|
|
2
5
|
import {stripSpeechFormatting} from 'arcane-os/speech-text';
|
|
3
6
|
|
|
@@ -40,15 +43,15 @@ function splitSpeechText(value=''){
|
|
|
40
43
|
}
|
|
41
44
|
|
|
42
45
|
function normalizeParts(parts,defaultVoice=null,defaultSpeed=1){
|
|
43
|
-
if(!
|
|
46
|
+
if(!is.array(parts)||!parts.length){
|
|
44
47
|
throw new TypeError('Narration text cannot be blank. The full visual content remains available.');
|
|
45
48
|
}
|
|
46
49
|
return parts.map(function normalizePart(part){
|
|
47
|
-
const candidate=
|
|
50
|
+
const candidate=is.string(part)?{input:part}:part;
|
|
48
51
|
const input=String(candidate?.input??'');
|
|
49
52
|
if(!input.trim())throw new TypeError('Speech segments cannot be blank. The full visual content remains available.');
|
|
50
53
|
const pauseAfterMs=Number(candidate?.pauseAfterMs??0);
|
|
51
|
-
if(!
|
|
54
|
+
if(!is.finite(pauseAfterMs)||pauseAfterMs<0){
|
|
52
55
|
throw new RangeError('Speech segment pauses must be a nonnegative number of milliseconds.');
|
|
53
56
|
}
|
|
54
57
|
const voiceValue=candidate?.voice??defaultVoice;
|
|
@@ -57,7 +60,7 @@ function normalizeParts(parts,defaultVoice=null,defaultSpeed=1){
|
|
|
57
60
|
if(voiceValue!==null&&voiceValue!==undefined&&!voice.trim()){
|
|
58
61
|
throw new TypeError('Speech segment voice cannot be blank.');
|
|
59
62
|
}
|
|
60
|
-
if(!
|
|
63
|
+
if(!is.finite(speed)||speed<=0)throw new RangeError('Every speech segment requires a positive speech speed.');
|
|
61
64
|
return {
|
|
62
65
|
input,
|
|
63
66
|
pauseAfterMs,
|
|
@@ -84,7 +87,7 @@ function synthesizedAudioContractError(cause=null){
|
|
|
84
87
|
|
|
85
88
|
function normalizeAudioContentType(value,fallback=null){
|
|
86
89
|
const candidate=value===undefined||value===null?fallback:value;
|
|
87
|
-
if(
|
|
90
|
+
if(!is.string(candidate)||!candidate.trim()){
|
|
88
91
|
throw synthesizedAudioContractError();
|
|
89
92
|
}
|
|
90
93
|
const contentType=candidate.split(';',1)[0].trim().toLowerCase();
|
|
@@ -103,7 +106,7 @@ function speechAudioBytes(value){
|
|
|
103
106
|
if(value instanceof ArrayBuffer){
|
|
104
107
|
return new Uint8Array(value.slice(0));
|
|
105
108
|
}
|
|
106
|
-
if(
|
|
109
|
+
if(is.arrayBufferView(value)){
|
|
107
110
|
return new Uint8Array(
|
|
108
111
|
value.buffer.slice(
|
|
109
112
|
value.byteOffset,
|
|
@@ -203,23 +206,23 @@ async function validatedSpeechBytes(bytes,contentType='audio/wav'){
|
|
|
203
206
|
async function playableSpeechBlob(response){
|
|
204
207
|
try{
|
|
205
208
|
if(response instanceof Blob)return validatedSpeechBlob(response);
|
|
206
|
-
if(response instanceof ArrayBuffer||
|
|
209
|
+
if(response instanceof ArrayBuffer||is.arrayBufferView(response)){
|
|
207
210
|
return validatedSpeechBytes(speechAudioBytes(response));
|
|
208
211
|
}
|
|
209
|
-
if(response&&
|
|
212
|
+
if(response&&is.object(response)){
|
|
210
213
|
if(response.audio instanceof Blob){
|
|
211
214
|
return validatedSpeechBlob(
|
|
212
215
|
response.audio,
|
|
213
216
|
response.contentType
|
|
214
217
|
);
|
|
215
218
|
}
|
|
216
|
-
if(response.audio instanceof ArrayBuffer||
|
|
219
|
+
if(response.audio instanceof ArrayBuffer||is.arrayBufferView(response.audio)){
|
|
217
220
|
return validatedSpeechBytes(
|
|
218
221
|
speechAudioBytes(response.audio),
|
|
219
222
|
response.contentType
|
|
220
223
|
);
|
|
221
224
|
}
|
|
222
|
-
if(
|
|
225
|
+
if(is.string(response.audioBase64)&&response.audioBase64){
|
|
223
226
|
return validatedSpeechBytes(
|
|
224
227
|
base64Bytes(response.audioBase64),
|
|
225
228
|
response.contentType
|
|
@@ -238,7 +241,7 @@ async function playableSpeechBlob(response){
|
|
|
238
241
|
|
|
239
242
|
function speechPlaybackFailureReason(error){
|
|
240
243
|
if(error?.name==='AbortError')return 'speech-synthesis-cancelled';
|
|
241
|
-
if(
|
|
244
|
+
if(is.string(error?.code)
|
|
242
245
|
&&Object.hasOwn(SPEECH_PLAYBACK_FAILURE_REASONS,error.code)){
|
|
243
246
|
return SPEECH_PLAYBACK_FAILURE_REASONS[error.code];
|
|
244
247
|
}
|
|
@@ -295,7 +298,7 @@ class LatestSpeechQueue{
|
|
|
295
298
|
}
|
|
296
299
|
|
|
297
300
|
function queueFor(speech){
|
|
298
|
-
if((
|
|
301
|
+
if((!is.object(speech)||speech===null)&&!is.function(speech)){
|
|
299
302
|
throw new TypeError('A local speech client is required.');
|
|
300
303
|
}
|
|
301
304
|
let queue=queuesBySpeechClient.get(speech);
|
|
@@ -307,15 +310,15 @@ function queueFor(speech){
|
|
|
307
310
|
}
|
|
308
311
|
|
|
309
312
|
function providerSpeechCapacity(speech){
|
|
310
|
-
if(
|
|
313
|
+
if(!is.function(speech?.fetchTTS))return null;
|
|
311
314
|
try{
|
|
312
315
|
const providerRuntime=speech.providerRuntime;
|
|
313
|
-
if(!providerRuntime
|
|
316
|
+
if(!providerRuntime||!is.function(providerRuntime.status))return null;
|
|
314
317
|
const capacity=providerRuntime.status(
|
|
315
318
|
'tts',
|
|
316
319
|
{execution:true}
|
|
317
320
|
)?.execution?.maxConcurrentRequests;
|
|
318
|
-
return
|
|
321
|
+
return is.safeInteger(capacity)&&capacity>0?capacity:null;
|
|
319
322
|
}catch{
|
|
320
323
|
// Clients without inspectable provider capacity retain serialized admission.
|
|
321
324
|
return null;
|
|
@@ -424,11 +427,11 @@ class SpeechPlayback{
|
|
|
424
427
|
delay,
|
|
425
428
|
messages={}
|
|
426
429
|
}={}){
|
|
427
|
-
if(!audio
|
|
430
|
+
if(!audio||!is.function(audio.addEventListener)){
|
|
428
431
|
throw new TypeError('SpeechPlayback requires an audio element.');
|
|
429
432
|
}
|
|
430
433
|
const normalizedSpeed=Number(speed);
|
|
431
|
-
if(!
|
|
434
|
+
if(!is.finite(normalizedSpeed)||normalizedSpeed<=0){
|
|
432
435
|
throw new RangeError('SpeechPlayback speed must be a positive number.');
|
|
433
436
|
}
|
|
434
437
|
this.audio=audio;
|
|
@@ -496,7 +499,7 @@ class SpeechPlayback{
|
|
|
496
499
|
|
|
497
500
|
message(name,details={}){
|
|
498
501
|
const value=this.messages[name];
|
|
499
|
-
return String(
|
|
502
|
+
return String(is.function(value)?value(details):value||'');
|
|
500
503
|
}
|
|
501
504
|
|
|
502
505
|
nextOperationId(){
|
|
@@ -537,7 +540,7 @@ class SpeechPlayback{
|
|
|
537
540
|
try{
|
|
538
541
|
this.onState(detail);
|
|
539
542
|
}catch(error){
|
|
540
|
-
if(
|
|
543
|
+
if(is.function(globalThis.reportError))globalThis.reportError(error);
|
|
541
544
|
else console.error(error);
|
|
542
545
|
}
|
|
543
546
|
return detail;
|
|
@@ -547,8 +550,8 @@ class SpeechPlayback{
|
|
|
547
550
|
return Boolean(
|
|
548
551
|
this.speech
|
|
549
552
|
&&(
|
|
550
|
-
|
|
551
|
-
||
|
|
553
|
+
is.function(this.speech.fetchTTS)
|
|
554
|
+
||is.function(this.speech.synthesize)
|
|
552
555
|
)
|
|
553
556
|
);
|
|
554
557
|
}
|
|
@@ -603,12 +606,12 @@ class SpeechPlayback{
|
|
|
603
606
|
...(part.voice?{voice:part.voice}:{}),
|
|
604
607
|
...(this.responseFormat?{responseFormat:this.responseFormat}:{})
|
|
605
608
|
};
|
|
606
|
-
if(
|
|
609
|
+
if(is.function(this.speech?.fetchTTS)){
|
|
607
610
|
return playableSpeechBlob(
|
|
608
611
|
await this.speech.fetchTTS(payload,signal,preparation)
|
|
609
612
|
);
|
|
610
613
|
}
|
|
611
|
-
if(
|
|
614
|
+
if(is.function(this.speech?.synthesize)){
|
|
612
615
|
return playableSpeechBlob(
|
|
613
616
|
await this.speech.synthesize(payload,{signal},preparation)
|
|
614
617
|
);
|
|
@@ -783,7 +786,7 @@ class SpeechPlayback{
|
|
|
783
786
|
throw error;
|
|
784
787
|
}
|
|
785
788
|
const numericSpeed=Number(speed);
|
|
786
|
-
if(!
|
|
789
|
+
if(!is.finite(numericSpeed)||numericSpeed<=0)throw new RangeError('Speech speed must be a positive number.');
|
|
787
790
|
const selectedModel=model===null||model===undefined
|
|
788
791
|
?null
|
|
789
792
|
:String(model);
|
|
@@ -807,7 +810,7 @@ class SpeechPlayback{
|
|
|
807
810
|
this.voice=selectedVoice;
|
|
808
811
|
this.responseFormat=selectedResponseFormat;
|
|
809
812
|
normalized=normalizeParts(
|
|
810
|
-
|
|
813
|
+
is.function(parts)?parts():parts,
|
|
811
814
|
selectedVoice,
|
|
812
815
|
numericSpeed
|
|
813
816
|
);
|
|
@@ -1007,7 +1010,7 @@ class SpeechPlayback{
|
|
|
1007
1010
|
|
|
1008
1011
|
fail(error){
|
|
1009
1012
|
const reason=speechPlaybackFailureReason(error);
|
|
1010
|
-
const code=
|
|
1013
|
+
const code=is.string(error?.code)&&error.code.trim()
|
|
1011
1014
|
?error.code.trim()
|
|
1012
1015
|
:reason==='speech-playback-request-contract-mismatch'
|
|
1013
1016
|
?'ARCANE_SPEECH_PLAYBACK_REQUEST_CONTRACT_MISMATCH'
|
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
import Is from 'strong-type';
|
|
2
|
+
const is=new Is(false);
|
|
3
|
+
|
|
1
4
|
import DocumentLexicalSearch,{
|
|
2
5
|
documentContextExcerpt,
|
|
3
6
|
documentSearchTokens,
|
|
@@ -16,8 +19,8 @@ const MEDIA_TYPES=new Set(['text/markdown','text/plain']);
|
|
|
16
19
|
|
|
17
20
|
function isPlainRecord(value){
|
|
18
21
|
return Boolean(value)
|
|
19
|
-
&&
|
|
20
|
-
&&!
|
|
22
|
+
&&is.object(value)
|
|
23
|
+
&&!is.array(value)
|
|
21
24
|
&&Object.getPrototypeOf(value)===Object.prototype;
|
|
22
25
|
}
|
|
23
26
|
|
|
@@ -36,7 +39,7 @@ function assertKnownKeys(value,allowed,label,code='STATIC_DOCUMENT_INVALID_CATAL
|
|
|
36
39
|
}
|
|
37
40
|
|
|
38
41
|
function structuralInteger(value,label,{minimum=0,maximum=null}={}){
|
|
39
|
-
if(!
|
|
42
|
+
if(!is.safeInteger(value)||value<minimum||(maximum!==null&&value>maximum)){
|
|
40
43
|
const range=maximum===null?`${minimum} or greater`:`${minimum} through ${maximum}`;
|
|
41
44
|
fail(`${label} must be a safe integer ${range}.`,'STATIC_DOCUMENT_INVALID_VALUE',RangeError);
|
|
42
45
|
}
|
|
@@ -45,7 +48,7 @@ function structuralInteger(value,label,{minimum=0,maximum=null}={}){
|
|
|
45
48
|
|
|
46
49
|
function normalizedText(value,label,{optional=false}={}){
|
|
47
50
|
if(optional&&(value===undefined||value===null||value==='')) return '';
|
|
48
|
-
if(
|
|
51
|
+
if(!is.string(value)) fail(`${label} must be a string.`,'STATIC_DOCUMENT_INVALID_CATALOG');
|
|
49
52
|
if(!value.trim()&&!optional) fail(`${label} cannot be empty.`,'STATIC_DOCUMENT_INVALID_CATALOG');
|
|
50
53
|
if(CONTROL_CHARACTERS.test(value)) fail(`${label} cannot contain control characters.`,'STATIC_DOCUMENT_INVALID_CATALOG');
|
|
51
54
|
if(value!==value.normalize('NFC')) fail(`${label} must use Unicode NFC normalization.`,'STATIC_DOCUMENT_INVALID_CATALOG');
|
|
@@ -93,7 +96,7 @@ function relativePath(value,label='Document path'){
|
|
|
93
96
|
|
|
94
97
|
function normalizeTags(value){
|
|
95
98
|
if(value===undefined) return [];
|
|
96
|
-
if(!
|
|
99
|
+
if(!is.array(value))fail('Document tags must be an array.','STATIC_DOCUMENT_INVALID_CATALOG');
|
|
97
100
|
const seen=new Set();
|
|
98
101
|
const tags=value.map((item,index)=>{
|
|
99
102
|
const tag=normalizedText(item,`Document tag ${index+1}`);
|
|
@@ -107,7 +110,7 @@ function normalizeTags(value){
|
|
|
107
110
|
|
|
108
111
|
function normalizeTextList(value,label){
|
|
109
112
|
if(value===undefined) return [];
|
|
110
|
-
if(!
|
|
113
|
+
if(!is.array(value))fail(`${label} must be an array.`,'STATIC_DOCUMENT_INVALID_CATALOG');
|
|
111
114
|
const seen=new Set();
|
|
112
115
|
const values=value.map(function normalizeTextListEntry(item,index){
|
|
113
116
|
const text=normalizedText(item,`${label} entry ${index+1}`);
|
|
@@ -149,7 +152,7 @@ function normalizeLanguage(value,label){
|
|
|
149
152
|
|
|
150
153
|
function normalizeSearchTerms(value,label){
|
|
151
154
|
if(value===undefined) return [];
|
|
152
|
-
if(!
|
|
155
|
+
if(!is.array(value))fail(`${label} must be an array.`,'STATIC_DOCUMENT_INVALID_CATALOG');
|
|
153
156
|
const seen=new Set();
|
|
154
157
|
const terms=value.map((item,index)=>{
|
|
155
158
|
const term=normalizedText(item,`${label} entry ${index+1}`);
|
|
@@ -163,7 +166,7 @@ function normalizeSearchTerms(value,label){
|
|
|
163
166
|
|
|
164
167
|
function normalizeHeadings(value){
|
|
165
168
|
if(value===undefined) return [];
|
|
166
|
-
if(!
|
|
169
|
+
if(!is.array(value))fail('Document headings must be an array.','STATIC_DOCUMENT_INVALID_CATALOG');
|
|
167
170
|
const seen=new Set();
|
|
168
171
|
const headings=value.map((item,index)=>{
|
|
169
172
|
if(!isPlainRecord(item)) fail(`Document heading ${index+1} must be a plain object.`,'STATIC_DOCUMENT_INVALID_CATALOG');
|
|
@@ -184,7 +187,7 @@ function normalizeHeadings(value){
|
|
|
184
187
|
|
|
185
188
|
function normalizePathList(value,label){
|
|
186
189
|
if(value===undefined) return [];
|
|
187
|
-
if(!
|
|
190
|
+
if(!is.array(value))fail(`${label} must be an array.`,'STATIC_DOCUMENT_INVALID_CATALOG');
|
|
188
191
|
const seen=new Set();
|
|
189
192
|
const paths=value.map((item,index)=>{
|
|
190
193
|
const path=relativePath(item,`${label} entry ${index+1}`);
|
|
@@ -293,7 +296,7 @@ function normalizeStaticDocumentCatalog(input,options={}){
|
|
|
293
296
|
if(!isPlainRecord(options)) fail('Catalog normalization options must be a plain object.','STATIC_DOCUMENT_INVALID_OPTIONS');
|
|
294
297
|
if(!isPlainRecord(input)) fail('Static document catalog must be a plain object.','STATIC_DOCUMENT_INVALID_CATALOG');
|
|
295
298
|
assertKnownKeys(input,new Set(['documents','version']),'Static document catalog');
|
|
296
|
-
if(!
|
|
299
|
+
if(!is.array(input.documents)) fail('Static document catalog documents must be an array.','STATIC_DOCUMENT_INVALID_CATALOG');
|
|
297
300
|
const records=input.documents.map((record,index)=>normalizeRecord(record,index));
|
|
298
301
|
const ids=new Set();
|
|
299
302
|
const paths=new Set();
|
|
@@ -384,11 +387,11 @@ function normalizeOptions(input){
|
|
|
384
387
|
cacheTimeoutMs:optionalTimeout(input.cacheTimeoutMs,'cacheTimeoutMs'),
|
|
385
388
|
fetchTimeoutMs:optionalTimeout(input.fetchTimeoutMs,'fetchTimeoutMs'),
|
|
386
389
|
});
|
|
387
|
-
const fetchImpl=input.fetchImpl??(
|
|
388
|
-
if(fetchImpl!==null
|
|
389
|
-
if(input.onCacheError!==undefined
|
|
390
|
+
const fetchImpl=input.fetchImpl??(is.function(globalThis.fetch)?globalThis.fetch.bind(globalThis):null);
|
|
391
|
+
if(fetchImpl!==null&&!is.function(fetchImpl)) fail('fetchImpl must be a function when provided.','STATIC_DOCUMENT_INVALID_OPTIONS');
|
|
392
|
+
if(input.onCacheError!==undefined&&!is.function(input.onCacheError)) fail('onCacheError must be a function when provided.','STATIC_DOCUMENT_INVALID_OPTIONS');
|
|
390
393
|
const cache=input.cache??null;
|
|
391
|
-
if(cache!==null&&(
|
|
394
|
+
if(cache!==null&&(!is.object(cache)||!is.function(cache.get)||!is.function(cache.set))){
|
|
392
395
|
fail('cache must expose get(key) and set(key, value).','STATIC_DOCUMENT_INVALID_OPTIONS');
|
|
393
396
|
}
|
|
394
397
|
return completeValue({
|
|
@@ -411,7 +414,7 @@ function lineNumberAt(value,offset){
|
|
|
411
414
|
}
|
|
412
415
|
|
|
413
416
|
function queryText(value){
|
|
414
|
-
if(
|
|
417
|
+
if(!is.string(value)) fail('Search query must be a string.','STATIC_DOCUMENT_INVALID_QUERY');
|
|
415
418
|
const query=value.trim();
|
|
416
419
|
if(CONTROL_CHARACTERS.test(query)){
|
|
417
420
|
fail('Search query must be plain text.','STATIC_DOCUMENT_INVALID_QUERY');
|
|
@@ -421,7 +424,7 @@ function queryText(value){
|
|
|
421
424
|
|
|
422
425
|
function normalizeFilter(value,label){
|
|
423
426
|
if(value===undefined) return null;
|
|
424
|
-
if(!
|
|
427
|
+
if(!is.array(value)) fail(`${label} must be an array.`,'STATIC_DOCUMENT_INVALID_QUERY');
|
|
425
428
|
const normalized=value.map((item,index)=>normalizedText(item,`${label} entry ${index+1}`).toLowerCase());
|
|
426
429
|
return new Set(normalized);
|
|
427
430
|
}
|
|
@@ -442,19 +445,19 @@ function staticDocumentCacheKey(version,id){
|
|
|
442
445
|
}
|
|
443
446
|
|
|
444
447
|
async function responseText(response){
|
|
445
|
-
if(
|
|
446
|
-
if(response instanceof ArrayBuffer||
|
|
448
|
+
if(is.string(response))return response;
|
|
449
|
+
if(response instanceof ArrayBuffer||is.arrayBufferView(response)){
|
|
447
450
|
try{return new TextDecoder('utf-8',{fatal:true}).decode(response);}
|
|
448
451
|
catch{fail('Document response is not valid UTF-8 text.','STATIC_DOCUMENT_INVALID_TEXT');}
|
|
449
452
|
}
|
|
450
|
-
if(!response
|
|
453
|
+
if(!response||!is.object(response)) fail('fetchImpl returned an invalid response.','STATIC_DOCUMENT_INVALID_RESPONSE');
|
|
451
454
|
if('ok' in response&&!response.ok){
|
|
452
455
|
fail(`Document request failed (${Number(response.status)||0}).`,'STATIC_DOCUMENT_HTTP_ERROR');
|
|
453
456
|
}
|
|
454
|
-
if(
|
|
457
|
+
if(is.function(response.arrayBuffer)){
|
|
455
458
|
return responseText(await response.arrayBuffer());
|
|
456
459
|
}
|
|
457
|
-
if(
|
|
460
|
+
if(is.function(response.text))return response.text();
|
|
458
461
|
if(response.body?.getReader){
|
|
459
462
|
const reader=response.body.getReader();
|
|
460
463
|
const decoder=new TextDecoder('utf-8',{fatal:true});
|
|
@@ -488,10 +491,10 @@ function abortError(message='The document request was aborted.'){
|
|
|
488
491
|
|
|
489
492
|
function signalLike(value){
|
|
490
493
|
return value===undefined||value===null||(
|
|
491
|
-
|
|
492
|
-
&&
|
|
493
|
-
&&
|
|
494
|
-
&&
|
|
494
|
+
is.object(value)
|
|
495
|
+
&&is.boolean(value.aborted)
|
|
496
|
+
&&is.function(value.addEventListener)
|
|
497
|
+
&&is.function(value.removeEventListener)
|
|
495
498
|
);
|
|
496
499
|
}
|
|
497
500
|
|
|
@@ -531,7 +534,7 @@ function timedOperation(operation,{milliseconds,signal}){
|
|
|
531
534
|
function hydrationOptions(input){
|
|
532
535
|
if(!isPlainRecord(input)) fail('Hydration options must be a plain object.','STATIC_DOCUMENT_INVALID_OPTIONS');
|
|
533
536
|
assertKnownKeys(input,new Set(['bypassCache','signal']),'Hydration options','STATIC_DOCUMENT_INVALID_OPTIONS');
|
|
534
|
-
if(input.bypassCache!==undefined
|
|
537
|
+
if(input.bypassCache!==undefined&&!is.boolean(input.bypassCache)) fail('bypassCache must be a boolean.','STATIC_DOCUMENT_INVALID_OPTIONS');
|
|
535
538
|
if(!signalLike(input.signal)) fail('signal must be an AbortSignal.','STATIC_DOCUMENT_INVALID_OPTIONS');
|
|
536
539
|
return {bypassCache:Boolean(input.bypassCache),signal:input.signal??null};
|
|
537
540
|
}
|
|
@@ -545,7 +548,7 @@ function normalizedError(error){
|
|
|
545
548
|
}
|
|
546
549
|
}catch{}
|
|
547
550
|
return completeValue({
|
|
548
|
-
code:
|
|
551
|
+
code:is.string(error?.code)?error.code:'STATIC_DOCUMENT_ERROR',
|
|
549
552
|
message,
|
|
550
553
|
});
|
|
551
554
|
}
|
|
@@ -615,7 +618,7 @@ export default class StaticDocumentCatalog{
|
|
|
615
618
|
}
|
|
616
619
|
|
|
617
620
|
get(id){
|
|
618
|
-
if(
|
|
621
|
+
if(!is.string(id)||!ID_PATTERN.test(id)) fail('Document id is invalid.','STATIC_DOCUMENT_INVALID_ID');
|
|
619
622
|
return this.#recordsById.get(id)??null;
|
|
620
623
|
}
|
|
621
624
|
|
|
@@ -685,7 +688,7 @@ export default class StaticDocumentCatalog{
|
|
|
685
688
|
||entry.schemaVersion!==CATALOG_SCHEMA_VERSION
|
|
686
689
|
||entry.catalogVersion!==this.version
|
|
687
690
|
||entry.documentId!==record.id
|
|
688
|
-
|
|
691
|
+
||!is.string(entry.text)
|
|
689
692
|
) fail('Cached document metadata is invalid.','STATIC_DOCUMENT_CACHE_INVALID');
|
|
690
693
|
return this.#retainHydration(record,entry.text,this.#resolve(record).href);
|
|
691
694
|
}catch(error){
|
|
@@ -745,7 +748,7 @@ export default class StaticDocumentCatalog{
|
|
|
745
748
|
async buildContext(query,options={}){
|
|
746
749
|
if(!isPlainRecord(options)) fail('Context options must be a plain object.','STATIC_DOCUMENT_INVALID_OPTIONS');
|
|
747
750
|
if(!signalLike(options.signal)) fail('signal must be an AbortSignal.','STATIC_DOCUMENT_INVALID_OPTIONS');
|
|
748
|
-
if(options.bodySearch!==undefined
|
|
751
|
+
if(options.bodySearch!==undefined&&!is.boolean(options.bodySearch)) fail('bodySearch must be a boolean.','STATIC_DOCUMENT_INVALID_OPTIONS');
|
|
749
752
|
const queryValue=queryText(query);
|
|
750
753
|
const bodySearch=Boolean(options.bodySearch)&&Boolean(queryValue);
|
|
751
754
|
const indexedMatches=this.#lexicalSearch.rank(queryValue);
|
|
@@ -1,10 +1,13 @@
|
|
|
1
|
+
import Is from 'strong-type';
|
|
2
|
+
const is=new Is(false);
|
|
3
|
+
|
|
1
4
|
export default class SystemAppearance{
|
|
2
5
|
constructor(api=globalThis.Arcane?.appearance||null){ this.api=api; }
|
|
3
6
|
|
|
4
|
-
available(){ return Boolean(this.api&&
|
|
7
|
+
available(){ return Boolean(this.api&&is.function(this.api.apply)); }
|
|
5
8
|
|
|
6
9
|
async current(){
|
|
7
|
-
if(!this.api
|
|
10
|
+
if(!this.api||!is.function(this.api.current)) return {supported:false,platform:'browser'};
|
|
8
11
|
return this.api.current();
|
|
9
12
|
}
|
|
10
13
|
|
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
import Is from 'strong-type';
|
|
2
|
+
const is=new Is(false);
|
|
3
|
+
|
|
1
4
|
const TOOL_PATTERN=/^[a-z][a-z0-9-]{0,63}$/;
|
|
2
5
|
|
|
3
6
|
function quoteArgument(value){
|
|
@@ -11,7 +14,7 @@ export default class SystemToolRegistry{
|
|
|
11
14
|
register(definition={}){
|
|
12
15
|
const id=String(definition.id||'').trim().toLowerCase();
|
|
13
16
|
if(!TOOL_PATTERN.test(id)||this.tools.has(id))throw new TypeError(`Duplicate or invalid system tool: ${id}`);
|
|
14
|
-
if(
|
|
17
|
+
if(!is.function(definition.command)&&!is.string(definition.command))throw new TypeError(`System tool ${id} requires a command builder.`);
|
|
15
18
|
const record=Object.freeze({id,label:String(definition.label||id),description:String(definition.description||''),usage:String(definition.usage||id),command:definition.command});
|
|
16
19
|
this.tools.set(id,record);return record;
|
|
17
20
|
}
|
|
@@ -19,8 +22,8 @@ export default class SystemToolRegistry{
|
|
|
19
22
|
get(id){return this.tools.get(String(id||'').toLowerCase())||null;}
|
|
20
23
|
build(id,args=[]){
|
|
21
24
|
const tool=this.get(id);if(!tool)throw new RangeError(`Unknown system tool: ${id}`);
|
|
22
|
-
const command=
|
|
23
|
-
if(
|
|
25
|
+
const command=is.function(tool.command)?tool.command(Array.from(args)):tool.command;
|
|
26
|
+
if(!is.string(command)||!command.trim())throw new TypeError(`System tool ${id} produced an empty command.`);
|
|
24
27
|
return command.trim();
|
|
25
28
|
}
|
|
26
29
|
}
|
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
import Is from 'strong-type';
|
|
2
|
+
const is=new Is(false);
|
|
3
|
+
|
|
1
4
|
import {createArcaneEventSource} from 'arcane-os/event-manager';
|
|
2
5
|
import TerminalSession from '../entities/TerminalSession.js';
|
|
3
6
|
|
|
@@ -47,17 +50,17 @@ function terminalEventReason(type){
|
|
|
47
50
|
|
|
48
51
|
function terminalPublicDetail(detail,type,reason){
|
|
49
52
|
const id=detail?.session?.id??detail?.sessionId;
|
|
50
|
-
const output=
|
|
53
|
+
const output=is.string(detail?.data)?detail.data:'';
|
|
51
54
|
const code=type==='error'
|
|
52
|
-
?
|
|
55
|
+
?is.string(detail?.code)&&detail.code
|
|
53
56
|
?detail.code
|
|
54
57
|
:TERMINAL_CLIENT_ERROR_CODES.sessionHostError
|
|
55
58
|
:null;
|
|
56
59
|
return {
|
|
57
|
-
...(
|
|
58
|
-
...(
|
|
59
|
-
...(
|
|
60
|
-
...(
|
|
60
|
+
...(is.string(id)&&id?{id}:{}),
|
|
61
|
+
...(is.string(detail?.stream)?{stream:detail.stream}:{}),
|
|
62
|
+
...(is.string(detail?.data)?{data:output}:{}),
|
|
63
|
+
...(is.string(detail?.session?.state)?{status:detail.session.state}:{}),
|
|
61
64
|
...(code?{code}:{}),
|
|
62
65
|
reason
|
|
63
66
|
};
|
|
@@ -93,8 +96,8 @@ export default class TerminalClient extends EventTarget{
|
|
|
93
96
|
type,
|
|
94
97
|
receiveTerminalHostEvent
|
|
95
98
|
);
|
|
96
|
-
if(
|
|
97
|
-
|
|
99
|
+
if(!is.function(unsubscribe)
|
|
100
|
+
&&!is.function(unsubscribe?.dispose)){
|
|
98
101
|
throw terminalClientError(
|
|
99
102
|
TERMINAL_CLIENT_ERROR_CODES.hostEventSubscriptionInvalid,
|
|
100
103
|
TERMINAL_CLIENT_REASONS.hostEventSubscriptionInvalid,
|
|
@@ -144,7 +147,7 @@ export default class TerminalClient extends EventTarget{
|
|
|
144
147
|
|
|
145
148
|
#assertCapability(method){
|
|
146
149
|
this.#assertOpen();
|
|
147
|
-
if(
|
|
150
|
+
if(!is.function(this.api?.[method])){
|
|
148
151
|
throw terminalClientError(
|
|
149
152
|
TERMINAL_CLIENT_ERROR_CODES.capabilityUnavailable,
|
|
150
153
|
TERMINAL_CLIENT_REASONS.capabilityUnavailable,
|
|
@@ -163,7 +166,7 @@ export default class TerminalClient extends EventTarget{
|
|
|
163
166
|
let firstError=null;
|
|
164
167
|
for(const subscription of this.unsubscribe){
|
|
165
168
|
try{
|
|
166
|
-
if(
|
|
169
|
+
if(is.function(subscription))subscription();
|
|
167
170
|
else subscription.dispose();
|
|
168
171
|
}catch(error){
|
|
169
172
|
firstError??=error;
|
|
@@ -179,7 +182,7 @@ export default class TerminalClient extends EventTarget{
|
|
|
179
182
|
const result=await this.api.start(options);
|
|
180
183
|
if(this.#destroyed||generation!==this.#generation){
|
|
181
184
|
let cleanupError=null;
|
|
182
|
-
if(
|
|
185
|
+
if(is.string(result?.id)&&is.function(this.api?.close)){
|
|
183
186
|
try{
|
|
184
187
|
await this.api.close(result.id);
|
|
185
188
|
}catch(error){
|