arcane-os 0.3.0 → 0.3.2
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 +27 -0
- package/README.md +86 -117
- package/bin/arcane-test.mjs +170 -46
- package/browser-runtime/ai/browser-speech-artifacts.mjs +887 -909
- package/browser-runtime/ai/browser-speech-providers.mjs +96 -152
- package/browser-runtime/ai/browser-wasm-llm-provider.mjs +627 -819
- package/browser-runtime/ai/browser-wasm.mjs +24 -35
- package/browser-runtime/ai/browser-wllama-runtime.mjs +64 -316
- package/browser-runtime/ai/model-controller.mjs +584 -181
- package/browser-runtime/ai/speech-worker-client.mjs +8 -146
- package/browser-runtime/ai/speech-worker-runtime.mjs +643 -363
- package/browser-runtime/dom-event-instrumentation.mjs +55 -147
- package/browser-runtime/event-manager.mjs +239 -624
- package/package.json +5 -6
- package/runtime/arcane/components/app-bar.html +3 -15
- package/runtime/arcane/components/assistant-panel.html +10 -10
- package/runtime/arcane/components/calculator.html +1 -1
- package/runtime/arcane/components/chat.html +1359 -135
- package/runtime/arcane/components/conversation-view.html +2 -2
- package/runtime/arcane/components/document-inspector.html +11 -17
- package/runtime/arcane/components/file-manager.html +13 -56
- package/runtime/arcane/components/markdown-document.html +82 -281
- package/runtime/arcane/components/markdown-editor.html +7 -10
- package/runtime/arcane/components/media-embed.html +6 -6
- package/runtime/arcane/components/screen-capture.html +4 -4
- package/runtime/arcane/components/source-explanation.html +2 -2
- package/runtime/arcane/components/speech.html +112 -68
- package/runtime/arcane/components/terminal-workspace.html +4 -4
- package/runtime/arcane/components/theme-editor.html +1 -1
- package/runtime/arcane/components/unified-inbox.html +2 -2
- package/runtime/arcane/components/voice-transcription.html +31 -21
- package/runtime/arcane/entities/Calculation.js +2 -3
- package/runtime/arcane/entities/Chat.js +228 -43
- package/runtime/arcane/entities/Preference.js +3 -5
- package/runtime/arcane/entities/Weather.js +5 -5
- package/runtime/arcane/modules/AI.js +1050 -427
- package/runtime/arcane/modules/AIProviderRuntime.js +658 -363
- package/runtime/arcane/modules/AIResponseLength.js +9 -19
- package/runtime/arcane/modules/AIRuntimeState.js +109 -72
- package/runtime/arcane/modules/ArcaneNavigationPolicy.js +45 -32
- package/runtime/arcane/modules/BrowserTestSuite.js +78 -122
- package/runtime/arcane/modules/CalculatorEngine.js +9 -9
- package/runtime/arcane/modules/CommunicationAppController.js +3 -7
- package/runtime/arcane/modules/ComponentContracts.js +30 -32
- package/runtime/arcane/modules/ConfiguredAIChatSession.js +281 -230
- package/runtime/arcane/modules/ConversationActionItems.js +26 -59
- package/runtime/arcane/modules/ConversationClosingReport.js +34 -61
- package/runtime/arcane/modules/ConversationTimebox.js +27 -15
- package/runtime/arcane/modules/DBOPFSDocumentLibrary.js +152 -344
- package/runtime/arcane/modules/DocumentLexicalSearch.js +25 -91
- package/runtime/arcane/modules/HTMLImport.js +54 -1
- package/runtime/arcane/modules/IsolatedModelQuestionRunner.js +40 -203
- package/runtime/arcane/modules/LocalAIReadiness.js +40 -60
- package/runtime/arcane/modules/LocalAIReadinessController.js +15 -13
- package/runtime/arcane/modules/MD.js +1 -45
- package/runtime/arcane/modules/Mail.js +51 -103
- package/runtime/arcane/modules/MailOutbox.mjs +95 -193
- package/runtime/arcane/modules/MailTransport.mjs +36 -57
- package/runtime/arcane/modules/ModelDefinition.js +22 -106
- package/runtime/arcane/modules/OpenMeteoWeatherProvider.js +39 -101
- package/runtime/arcane/modules/PersistentAIChatSession.js +281 -18
- package/runtime/arcane/modules/PreferenceStore.js +102 -30
- package/runtime/arcane/modules/RiskSignalAnalyzer.js +8 -9
- package/runtime/arcane/modules/ScopedOPFSCache.js +7 -42
- package/runtime/arcane/modules/ScreenCapture.js +175 -128
- package/runtime/arcane/modules/SpeechPlayback.js +46 -149
- package/runtime/arcane/modules/StaticDocumentCatalog.js +173 -407
- package/runtime/arcane/modules/ToolCallRouter.js +25 -12
- package/runtime/arcane/modules/YouTubeMedia.js +6 -5
- package/schemas/arcane-app-bundle.schema.json +13 -78
- package/schemas/arcane-app.schema.json +9 -25
- package/schemas/arcane-lock.schema.json +18 -151
- package/schemas/arcane-package.schema.json +2 -16
- package/schemas/native-build-plan.schema.json +119 -122
- package/src/app-descriptor.mjs +75 -132
- package/src/application-tests.mjs +200 -0
- package/src/cli/main.mjs +27 -46
- package/src/constants.mjs +3 -4
- package/src/dev-server.mjs +30 -324
- package/src/doctor.mjs +92 -154
- package/src/dom-event-instrumentation.mjs +55 -147
- package/src/errors.mjs +2 -3
- package/src/event-manager.mjs +239 -624
- package/src/event-queue.mjs +3 -3
- package/src/import-map.mjs +273 -1028
- package/src/index.mjs +14 -16
- package/src/installed-sdk-runtime.mjs +40 -62
- package/src/integrated-provider-loader.mjs +53 -382
- package/src/mail-api.mjs +0 -2
- package/src/mail-server.mjs +224 -580
- package/src/mail.mjs +4 -10
- package/src/native-plan.mjs +163 -598
- package/src/native-provider-loader.mjs +104 -1063
- package/src/packager/core.mjs +485 -3229
- package/src/process.mjs +5 -10
- package/src/release-bundle.mjs +292 -2405
- package/src/runtime.mjs +76 -396
- package/src/scaffold.mjs +30 -80
- package/src/sdk-browser-runtime.mjs +70 -626
- package/src/source-server.mjs +588 -0
- package/src/targets/index.mjs +78 -188
- package/src/templates/workspace-template.mjs +19 -135
- package/src/testing-loader.mjs +164 -0
- package/src/testing.mjs +1 -1
- package/src/toolchain.mjs +131 -544
- package/src/update-check.mjs +26 -64
- package/src/workspace-operation-lock.mjs +139 -430
- package/src/workspace-runtime.mjs +112 -779
- package/src/workspace.mjs +40 -302
- package/browser-runtime/ARCANE_SDK_BROWSER_RELEASE.json +0 -218
- package/browser-runtime/ai/ARCANE_AI_BROWSER_SPEECH_COMPONENTS.json +0 -203
- package/browser-runtime/ai/ARCANE_AI_BROWSER_WASM_COMPONENTS.json +0 -80
- package/browser-runtime/ai/internal/sha256.mjs +0 -166
- package/docs/architecture.md +0 -344
- package/docs/compatibility.md +0 -36
- package/docs/event-manager.md +0 -294
- package/docs/platform-targets.md +0 -108
- package/docs/publishing.md +0 -201
- package/docs/reference/README.md +0 -187
- package/docs/reference/ai/browser-speech-package-authority.json +0 -835
- package/docs/reference/ai/browser-speech.md +0 -1252
- package/docs/reference/ai/browser-wasm.md +0 -530
- package/docs/reference/arcane-ollama.md +0 -288
- package/docs/reference/availability-and-normalization.md +0 -183
- package/docs/reference/behavioral-testing.md +0 -133
- package/docs/reference/cli.md +0 -779
- package/docs/reference/core/README.md +0 -62
- package/docs/reference/core/arcane-ai-contracts.md +0 -907
- package/docs/reference/core/arcane-api.md +0 -601
- package/docs/reference/core/arcane-entities.md +0 -65
- package/docs/reference/core/arcane-events.md +0 -134
- package/docs/reference/core/ollama-module.md +0 -181
- package/docs/reference/core/reference/arcane-api/ai-and-ollama.md +0 -1909
- package/docs/reference/core/reference/arcane-api/applications-terminal-capabilities.md +0 -1057
- package/docs/reference/core/reference/arcane-api/core-and-events.md +0 -320
- package/docs/reference/core/reference/arcane-api/filesystem-storage-preferences-appearance.md +0 -610
- package/docs/reference/core/reference/arcane-api/namespaces.md +0 -1157
- package/docs/reference/core/reference/arcane-api/platform-installation-users-system.md +0 -1423
- package/docs/reference/core/reference/arcane-api/session-provisioning-diagnostics-development.md +0 -315
- package/docs/reference/event-manager.md +0 -1511
- package/docs/reference/inventory/package-api.json +0 -3284
- package/docs/reference/inventory/runtime-components.json +0 -1011
- package/docs/reference/inventory/runtime-entities.json +0 -26
- package/docs/reference/inventory/runtime-modules.json +0 -1431
- package/docs/reference/mail.md +0 -316
- package/docs/reference/protocols.md +0 -677
- package/docs/reference/runtime-components.md +0 -1366
- package/docs/reference/runtime-entities.md +0 -303
- package/docs/reference/runtime-modules.md +0 -2960
- package/docs/reference/sdk-api.md +0 -6694
- package/docs/roadmap.md +0 -79
- package/docs/work-amplification.md +0 -129
- package/runtime/ARCANE_RUNTIME_RELEASE.json +0 -826
|
@@ -1,28 +1,20 @@
|
|
|
1
1
|
export const SPEECH_WORKER_PROTOCOL = "arcane-ai-speech-worker/1";
|
|
2
2
|
|
|
3
|
+
const completeValue = (value) => value;
|
|
4
|
+
|
|
3
5
|
const SPEECH_WORKER_ERROR_PROTOCOL = "arcane-ai-speech-worker-error/1";
|
|
4
6
|
|
|
5
7
|
const ARTIFACT_GRAPH_MODULE_GRAPH =
|
|
6
8
|
"browser-speech-authenticated-artifact-graph";
|
|
7
9
|
const GRAPH_GUARD_NAME = "__arcaneBrowserSpeechArtifactGraphGuardsV1";
|
|
10
|
+
const MODULE_ROUTER_NAME = "__arcaneBrowserSpeechModuleRouterV1";
|
|
8
11
|
const NESTED_WORKER_PROTOCOL =
|
|
9
12
|
"arcane-ai-browser-speech-artifact-module-worker/1";
|
|
10
|
-
const
|
|
11
|
-
"artifact-graph-network-dbopfs-verified",
|
|
12
|
-
"artifact-graph-dbopfs-cache-verified",
|
|
13
|
-
"artifact-graph-offline-dbopfs-cache-verified",
|
|
14
|
-
"artifact-graph-network-dbopfs-partially-checked",
|
|
15
|
-
"artifact-graph-dbopfs-cache-partially-checked",
|
|
16
|
-
"artifact-graph-offline-dbopfs-cache-partially-checked",
|
|
17
|
-
"artifact-graph-network-dbopfs-unchecked",
|
|
18
|
-
"artifact-graph-dbopfs-cache-unchecked",
|
|
19
|
-
"artifact-graph-offline-dbopfs-cache-unchecked",
|
|
20
|
-
]);
|
|
21
|
-
const ADAPTERS = Object.freeze({
|
|
13
|
+
const ADAPTERS = completeValue({
|
|
22
14
|
stt: "transformers-whisper",
|
|
23
15
|
tts: "kokoro-js",
|
|
24
16
|
});
|
|
25
|
-
const ONNX_NAMESPACES =
|
|
17
|
+
const ONNX_NAMESPACES = completeValue({
|
|
26
18
|
stt: "transformers-env-backends-onnx-wasm",
|
|
27
19
|
tts: "kokoro-env-wasm-paths",
|
|
28
20
|
});
|
|
@@ -30,7 +22,7 @@ const ARTIFACT_GRAPH_TRANSFORM_KINDS = new Set([
|
|
|
30
22
|
"function-return-this-to-global-this",
|
|
31
23
|
"typed-array-constructor",
|
|
32
24
|
]);
|
|
33
|
-
const PUBLIC_WORKER_OPERATIONS =
|
|
25
|
+
const PUBLIC_WORKER_OPERATIONS = completeValue([
|
|
34
26
|
"load",
|
|
35
27
|
"use",
|
|
36
28
|
"status",
|
|
@@ -41,16 +33,16 @@ const TRANSPORT_WORKER_OPERATION_SET = new Set([
|
|
|
41
33
|
...PUBLIC_WORKER_OPERATIONS,
|
|
42
34
|
"cancel",
|
|
43
35
|
]);
|
|
44
|
-
const BOTH_WORKER_ROLES =
|
|
45
|
-
const LOAD_OPERATION =
|
|
46
|
-
const USE_OPERATION =
|
|
47
|
-
const STATUS_OPERATION =
|
|
48
|
-
const UNLOAD_OPERATION =
|
|
49
|
-
const DISPOSE_OPERATION =
|
|
50
|
-
const UNLOAD_OR_DISPOSE_OPERATIONS =
|
|
51
|
-
const LOAD_OR_USE_OPERATIONS =
|
|
36
|
+
const BOTH_WORKER_ROLES = completeValue(["stt", "tts"]);
|
|
37
|
+
const LOAD_OPERATION = completeValue(["load"]);
|
|
38
|
+
const USE_OPERATION = completeValue(["use"]);
|
|
39
|
+
const STATUS_OPERATION = completeValue(["status"]);
|
|
40
|
+
const UNLOAD_OPERATION = completeValue(["unload"]);
|
|
41
|
+
const DISPOSE_OPERATION = completeValue(["dispose"]);
|
|
42
|
+
const UNLOAD_OR_DISPOSE_OPERATIONS = completeValue(["unload", "dispose"]);
|
|
43
|
+
const LOAD_OR_USE_OPERATIONS = completeValue(["load", "use"]);
|
|
52
44
|
const SDK_WORKER_ERRORS = new WeakSet();
|
|
53
|
-
const WORKER_ERROR_MESSAGES =
|
|
45
|
+
const WORKER_ERROR_MESSAGES = completeValue({
|
|
54
46
|
ARCANE_AI_REQUEST_ABORTED: "The speech worker operation was cancelled.",
|
|
55
47
|
ARCANE_AI_NOT_READY: "The speech worker is not loaded.",
|
|
56
48
|
ARCANE_AI_INVALID_REQUEST: "The speech worker request was rejected.",
|
|
@@ -77,10 +69,10 @@ const WORKER_ERROR_MESSAGES = Object.freeze({
|
|
|
77
69
|
const WORKER_ERROR_REASON_ADMISSIONS = new Map();
|
|
78
70
|
|
|
79
71
|
function registerWorkerErrorReasons(code, roles, operations, reasons) {
|
|
80
|
-
const admission =
|
|
72
|
+
const admission = completeValue({
|
|
81
73
|
code,
|
|
82
|
-
roles:
|
|
83
|
-
operations:
|
|
74
|
+
roles: completeValue([...roles]),
|
|
75
|
+
operations: completeValue([...operations]),
|
|
84
76
|
});
|
|
85
77
|
for (const reason of reasons) WORKER_ERROR_REASON_ADMISSIONS.set(reason, admission);
|
|
86
78
|
}
|
|
@@ -183,7 +175,7 @@ registerWorkerErrorReasons("ARCANE_AI_INVALID_REQUEST", ["tts"], LOAD_OPERATION,
|
|
|
183
175
|
"tts-default-voice-empty",
|
|
184
176
|
]);
|
|
185
177
|
registerWorkerErrorReasons("ARCANE_AI_INVALID_REQUEST", ["tts"], USE_OPERATION, [
|
|
186
|
-
"tts-synthesis-speed-
|
|
178
|
+
"tts-synthesis-speed-not-positive",
|
|
187
179
|
"tts-synthesis-text-empty",
|
|
188
180
|
"tts-synthesis-voice-empty",
|
|
189
181
|
"tts-synthesis-voice-not-declared",
|
|
@@ -465,7 +457,11 @@ function workerError(code, message, cause, reason) {
|
|
|
465
457
|
}
|
|
466
458
|
|
|
467
459
|
function isSdkWorkerError(value) {
|
|
468
|
-
|
|
460
|
+
try {
|
|
461
|
+
return value instanceof Error && SDK_WORKER_ERRORS.has(value);
|
|
462
|
+
} catch {
|
|
463
|
+
return false;
|
|
464
|
+
}
|
|
469
465
|
}
|
|
470
466
|
|
|
471
467
|
function admitWorkerFailure(error, code, message, reason) {
|
|
@@ -518,20 +514,237 @@ export function collectSpeechTransferables(value) {
|
|
|
518
514
|
}
|
|
519
515
|
|
|
520
516
|
function serializedError(error, role, op) {
|
|
521
|
-
const
|
|
522
|
-
|
|
517
|
+
const sdkError = isSdkWorkerError(error);
|
|
518
|
+
let reportedCode;
|
|
519
|
+
let reportedMessage;
|
|
520
|
+
let reportedReason;
|
|
521
|
+
let reportedCause;
|
|
522
|
+
try { reportedCode = Reflect.get(error, "code"); } catch {}
|
|
523
|
+
try { reportedMessage = Reflect.get(error, "message"); } catch {}
|
|
524
|
+
try { reportedReason = Reflect.get(error, "reason"); } catch {}
|
|
525
|
+
if (sdkError) {
|
|
526
|
+
try { reportedCause = Reflect.get(error, "cause"); } catch (cause) {
|
|
527
|
+
reportedCause = cause;
|
|
528
|
+
}
|
|
529
|
+
} else {
|
|
530
|
+
reportedCause = error;
|
|
531
|
+
}
|
|
532
|
+
const admission = sdkError
|
|
533
|
+
? workerErrorReasonAdmission(reportedReason, role, op)
|
|
523
534
|
: null;
|
|
524
|
-
const admittedCode =
|
|
525
|
-
&& Object.hasOwn(WORKER_ERROR_MESSAGES,
|
|
526
|
-
&& admission?.code ===
|
|
527
|
-
const code = admittedCode ?
|
|
528
|
-
const reason = admittedCode ?
|
|
529
|
-
|
|
535
|
+
const admittedCode = sdkError
|
|
536
|
+
&& Object.hasOwn(WORKER_ERROR_MESSAGES, reportedCode)
|
|
537
|
+
&& admission?.code === reportedCode;
|
|
538
|
+
const code = admittedCode ? reportedCode : "ARCANE_AI_PROVIDER_REQUEST_FAILED";
|
|
539
|
+
const reason = admittedCode ? reportedReason : operationFailureReason(role, op);
|
|
540
|
+
const message = typeof reportedMessage === "string" && reportedMessage.length > 0
|
|
541
|
+
? reportedMessage
|
|
542
|
+
: WORKER_ERROR_MESSAGES[code];
|
|
543
|
+
const envelope = completeValue({
|
|
530
544
|
protocol: SPEECH_WORKER_ERROR_PROTOCOL,
|
|
531
545
|
code,
|
|
532
|
-
message
|
|
546
|
+
message,
|
|
533
547
|
reason,
|
|
534
548
|
});
|
|
549
|
+
if (reportedCause !== undefined) {
|
|
550
|
+
envelope.cause = serializedDiagnosticValue(reportedCause);
|
|
551
|
+
}
|
|
552
|
+
return envelope;
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
function fallbackSerializedError(error, role, op) {
|
|
556
|
+
let message;
|
|
557
|
+
try { message = Reflect.get(error, "message"); } catch {}
|
|
558
|
+
return completeValue({
|
|
559
|
+
protocol: SPEECH_WORKER_ERROR_PROTOCOL,
|
|
560
|
+
code: "ARCANE_AI_PROVIDER_REQUEST_FAILED",
|
|
561
|
+
message: typeof message === "string" && message.length > 0
|
|
562
|
+
? message
|
|
563
|
+
: WORKER_ERROR_MESSAGES.ARCANE_AI_PROVIDER_REQUEST_FAILED,
|
|
564
|
+
reason: operationFailureReason(role, op),
|
|
565
|
+
});
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
function sendSerializedError(send, response, error, role, op, consoleScope) {
|
|
569
|
+
try {
|
|
570
|
+
response.error = serializedError(error, role, op);
|
|
571
|
+
send(response, []);
|
|
572
|
+
} catch (transportError) {
|
|
573
|
+
try {
|
|
574
|
+
consoleScope?.console?.error?.(
|
|
575
|
+
"The speech Worker could not transport its complete failure diagnostic.",
|
|
576
|
+
error,
|
|
577
|
+
transportError,
|
|
578
|
+
);
|
|
579
|
+
} catch {}
|
|
580
|
+
response.error = fallbackSerializedError(error, role, op);
|
|
581
|
+
send(response, []);
|
|
582
|
+
}
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
function diagnosticText(value, fallback) {
|
|
586
|
+
try {
|
|
587
|
+
return String(value);
|
|
588
|
+
} catch {
|
|
589
|
+
return fallback;
|
|
590
|
+
}
|
|
591
|
+
}
|
|
592
|
+
|
|
593
|
+
function defineDiagnosticProperty(target, key, value) {
|
|
594
|
+
Object.defineProperty(target, key, {
|
|
595
|
+
configurable: true,
|
|
596
|
+
enumerable: true,
|
|
597
|
+
value,
|
|
598
|
+
writable: true,
|
|
599
|
+
});
|
|
600
|
+
}
|
|
601
|
+
|
|
602
|
+
function diagnosticType(value) {
|
|
603
|
+
try {
|
|
604
|
+
return Object.prototype.toString.call(value).slice(8, -1);
|
|
605
|
+
} catch {
|
|
606
|
+
return "UninspectableObject";
|
|
607
|
+
}
|
|
608
|
+
}
|
|
609
|
+
|
|
610
|
+
function serializedDiagnosticValue(value, seen = new WeakMap()) {
|
|
611
|
+
if (value === null || value === undefined) return value;
|
|
612
|
+
const type = typeof value;
|
|
613
|
+
if (type !== "object" && type !== "function") {
|
|
614
|
+
return type === "symbol"
|
|
615
|
+
? diagnosticText(value, "[symbol could not be represented]")
|
|
616
|
+
: value;
|
|
617
|
+
}
|
|
618
|
+
if (seen.has(value)) return seen.get(value);
|
|
619
|
+
|
|
620
|
+
const result = Object.create(null);
|
|
621
|
+
seen.set(value, result);
|
|
622
|
+
let descriptors;
|
|
623
|
+
try {
|
|
624
|
+
descriptors = Object.getOwnPropertyDescriptors(value);
|
|
625
|
+
} catch (error) {
|
|
626
|
+
defineDiagnosticProperty(
|
|
627
|
+
result,
|
|
628
|
+
"inspectionError",
|
|
629
|
+
serializedDiagnosticValue(error, seen),
|
|
630
|
+
);
|
|
631
|
+
return result;
|
|
632
|
+
}
|
|
633
|
+
|
|
634
|
+
const symbolProperties = [];
|
|
635
|
+
for (const key of Reflect.ownKeys(descriptors)) {
|
|
636
|
+
const descriptor = descriptors[key];
|
|
637
|
+
const projected = Object.create(null);
|
|
638
|
+
if (Object.hasOwn(descriptor, "value")) {
|
|
639
|
+
defineDiagnosticProperty(
|
|
640
|
+
projected,
|
|
641
|
+
"value",
|
|
642
|
+
serializedDiagnosticValue(descriptor.value, seen),
|
|
643
|
+
);
|
|
644
|
+
} else {
|
|
645
|
+
defineDiagnosticProperty(projected, "kind", "accessor");
|
|
646
|
+
if (descriptor.get !== undefined) {
|
|
647
|
+
defineDiagnosticProperty(
|
|
648
|
+
projected,
|
|
649
|
+
"get",
|
|
650
|
+
diagnosticText(descriptor.get, "[getter could not be represented]"),
|
|
651
|
+
);
|
|
652
|
+
}
|
|
653
|
+
if (descriptor.set !== undefined) {
|
|
654
|
+
defineDiagnosticProperty(
|
|
655
|
+
projected,
|
|
656
|
+
"set",
|
|
657
|
+
diagnosticText(descriptor.set, "[setter could not be represented]"),
|
|
658
|
+
);
|
|
659
|
+
}
|
|
660
|
+
}
|
|
661
|
+
if (typeof key === "symbol") {
|
|
662
|
+
const symbolProperty = Object.create(null);
|
|
663
|
+
defineDiagnosticProperty(
|
|
664
|
+
symbolProperty,
|
|
665
|
+
"key",
|
|
666
|
+
diagnosticText(key, "[symbol key could not be represented]"),
|
|
667
|
+
);
|
|
668
|
+
defineDiagnosticProperty(symbolProperty, "descriptor", projected);
|
|
669
|
+
symbolProperties.push(symbolProperty);
|
|
670
|
+
continue;
|
|
671
|
+
}
|
|
672
|
+
defineDiagnosticProperty(
|
|
673
|
+
result,
|
|
674
|
+
key,
|
|
675
|
+
Object.hasOwn(projected, "value") ? projected.value : projected,
|
|
676
|
+
);
|
|
677
|
+
}
|
|
678
|
+
|
|
679
|
+
for (const key of ["name", "message", "stack", "code", "reason", "details", "cause"]) {
|
|
680
|
+
if (Object.hasOwn(result, key)) continue;
|
|
681
|
+
try {
|
|
682
|
+
const field = Reflect.get(value, key);
|
|
683
|
+
if (field !== undefined) {
|
|
684
|
+
defineDiagnosticProperty(
|
|
685
|
+
result,
|
|
686
|
+
key,
|
|
687
|
+
serializedDiagnosticValue(field, seen),
|
|
688
|
+
);
|
|
689
|
+
}
|
|
690
|
+
} catch (error) {
|
|
691
|
+
defineDiagnosticProperty(result, key, serializedDiagnosticValue(error, seen));
|
|
692
|
+
}
|
|
693
|
+
}
|
|
694
|
+
|
|
695
|
+
let metadataKey = "$diagnostic";
|
|
696
|
+
while (Object.hasOwn(result, metadataKey)) metadataKey = `$${metadataKey}`;
|
|
697
|
+
const metadata = Object.create(null);
|
|
698
|
+
const valueType = diagnosticType(value);
|
|
699
|
+
defineDiagnosticProperty(metadata, "type", valueType);
|
|
700
|
+
if (symbolProperties.length > 0) {
|
|
701
|
+
defineDiagnosticProperty(metadata, "symbolProperties", symbolProperties);
|
|
702
|
+
}
|
|
703
|
+
try {
|
|
704
|
+
if (type === "function") {
|
|
705
|
+
defineDiagnosticProperty(
|
|
706
|
+
metadata,
|
|
707
|
+
"source",
|
|
708
|
+
diagnosticText(value, "[function could not be represented]"),
|
|
709
|
+
);
|
|
710
|
+
} else if (valueType === "Map") {
|
|
711
|
+
const entries = [];
|
|
712
|
+
for (const [key, entry] of value) {
|
|
713
|
+
entries.push([
|
|
714
|
+
serializedDiagnosticValue(key, seen),
|
|
715
|
+
serializedDiagnosticValue(entry, seen),
|
|
716
|
+
]);
|
|
717
|
+
}
|
|
718
|
+
defineDiagnosticProperty(metadata, "entries", entries);
|
|
719
|
+
} else if (valueType === "Set") {
|
|
720
|
+
const entries = [];
|
|
721
|
+
for (const entry of value) entries.push(serializedDiagnosticValue(entry, seen));
|
|
722
|
+
defineDiagnosticProperty(metadata, "values", entries);
|
|
723
|
+
} else if (valueType === "Date") {
|
|
724
|
+
defineDiagnosticProperty(metadata, "value", Date.prototype.getTime.call(value));
|
|
725
|
+
} else if (valueType === "RegExp") {
|
|
726
|
+
defineDiagnosticProperty(metadata, "source", value.source);
|
|
727
|
+
defineDiagnosticProperty(metadata, "flags", value.flags);
|
|
728
|
+
defineDiagnosticProperty(metadata, "lastIndex", value.lastIndex);
|
|
729
|
+
} else if (valueType === "ArrayBuffer" || valueType === "SharedArrayBuffer"
|
|
730
|
+
|| valueType === "Blob" || valueType === "File") {
|
|
731
|
+
defineDiagnosticProperty(metadata, "value", value);
|
|
732
|
+
} else {
|
|
733
|
+
let view = false;
|
|
734
|
+
try {
|
|
735
|
+
view = ArrayBuffer.isView(value);
|
|
736
|
+
} catch {}
|
|
737
|
+
if (view) defineDiagnosticProperty(metadata, "value", value);
|
|
738
|
+
}
|
|
739
|
+
} catch (error) {
|
|
740
|
+
defineDiagnosticProperty(
|
|
741
|
+
metadata,
|
|
742
|
+
"inspectionError",
|
|
743
|
+
serializedDiagnosticValue(error, seen),
|
|
744
|
+
);
|
|
745
|
+
}
|
|
746
|
+
defineDiagnosticProperty(result, metadataKey, metadata);
|
|
747
|
+
return result;
|
|
535
748
|
}
|
|
536
749
|
|
|
537
750
|
function workerErrorReasonAdmission(reason, role, op) {
|
|
@@ -549,7 +762,10 @@ export function normalizeSpeechWorkerErrorEnvelope(value, role, op) {
|
|
|
549
762
|
try {
|
|
550
763
|
const keys = Reflect.ownKeys(value);
|
|
551
764
|
if (keys.some((key) => typeof key !== "string")
|
|
552
|
-
||
|
|
765
|
+
|| ![
|
|
766
|
+
"code,message,protocol,reason",
|
|
767
|
+
"cause,code,message,protocol,reason",
|
|
768
|
+
].includes(keys.sort().join(","))) return null;
|
|
553
769
|
descriptors = Object.getOwnPropertyDescriptors(value);
|
|
554
770
|
} catch {
|
|
555
771
|
return null;
|
|
@@ -563,13 +779,15 @@ export function normalizeSpeechWorkerErrorEnvelope(value, role, op) {
|
|
|
563
779
|
const reason = descriptors.reason.value;
|
|
564
780
|
if (protocol !== SPEECH_WORKER_ERROR_PROTOCOL) return null;
|
|
565
781
|
if (!Object.hasOwn(WORKER_ERROR_MESSAGES, code)) return null;
|
|
566
|
-
if (message !==
|
|
782
|
+
if (typeof message !== "string" || message.length < 1) return null;
|
|
567
783
|
const admission = workerErrorReasonAdmission(reason, role, op);
|
|
568
784
|
if (admission?.code !== code) return null;
|
|
569
|
-
|
|
785
|
+
if (descriptors.cause && !Object.hasOwn(descriptors.cause, "value")) return null;
|
|
786
|
+
return completeValue({
|
|
570
787
|
code,
|
|
571
788
|
message,
|
|
572
789
|
reason,
|
|
790
|
+
...(descriptors.cause ? { cause: descriptors.cause.value } : {}),
|
|
573
791
|
});
|
|
574
792
|
}
|
|
575
793
|
|
|
@@ -589,6 +807,22 @@ function requiredText(
|
|
|
589
807
|
return value.trim();
|
|
590
808
|
}
|
|
591
809
|
|
|
810
|
+
function requiredContent(
|
|
811
|
+
value,
|
|
812
|
+
label,
|
|
813
|
+
reason,
|
|
814
|
+
) {
|
|
815
|
+
if (typeof value !== "string" || !value.trim()) {
|
|
816
|
+
throw workerError(
|
|
817
|
+
"ARCANE_AI_INVALID_REQUEST",
|
|
818
|
+
`${label} is required.`,
|
|
819
|
+
undefined,
|
|
820
|
+
reason,
|
|
821
|
+
);
|
|
822
|
+
}
|
|
823
|
+
return value;
|
|
824
|
+
}
|
|
825
|
+
|
|
592
826
|
function requiredSampleRate(value, label, fallback) {
|
|
593
827
|
const candidate = value ?? fallback;
|
|
594
828
|
if (!Number.isSafeInteger(candidate) || candidate < 1) {
|
|
@@ -695,26 +929,6 @@ function validateConfiguration(configuration, role) {
|
|
|
695
929
|
paths.add(file.path);
|
|
696
930
|
}
|
|
697
931
|
const runtimePaths = new Set(configuration.runtime.files.map((file) => file.path));
|
|
698
|
-
if (
|
|
699
|
-
configuration.security?.secure !== true
|
|
700
|
-
|| typeof configuration.runtime.artifactGraphId !== "string"
|
|
701
|
-
|| !/^[a-f0-9]{64}$/u.test(configuration.runtime.artifactGraphId)
|
|
702
|
-
|| (
|
|
703
|
-
typeof configuration.runtime.guardCapability !== "string"
|
|
704
|
-
|| !/^[a-f0-9]{64}$/u.test(configuration.runtime.guardCapability)
|
|
705
|
-
|| !STRICT_GRAPH_ADMISSIONS.has(configuration.runtime.artifactGraphAdmission)
|
|
706
|
-
|| !configuration.runtime.edges
|
|
707
|
-
|| typeof configuration.runtime.edges !== "object"
|
|
708
|
-
|| !Array.isArray(configuration.runtime.transforms)
|
|
709
|
-
)
|
|
710
|
-
) {
|
|
711
|
-
throw workerError(
|
|
712
|
-
"ARCANE_AI_ARTIFACT_GRAPH_CONFIGURATION_INVALID",
|
|
713
|
-
"Explicit secure:true plus authenticated artifact graph identity, admission, edges, and transforms are required.",
|
|
714
|
-
undefined,
|
|
715
|
-
"artifact-graph-worker-configuration-incomplete",
|
|
716
|
-
);
|
|
717
|
-
}
|
|
718
932
|
const wasm = configuration.runtime.onnxWasm;
|
|
719
933
|
if (
|
|
720
934
|
!wasm
|
|
@@ -722,17 +936,17 @@ function validateConfiguration(configuration, role) {
|
|
|
722
936
|
|| !runtimePaths.has(wasm.mjsPath)
|
|
723
937
|
|| !runtimePaths.has(wasm.wasmPath)
|
|
724
938
|
) {
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
939
|
+
throw workerError(
|
|
940
|
+
"ARCANE_AI_ARTIFACT_GRAPH_CONFIGURATION_INVALID",
|
|
941
|
+
"The ONNX Runtime Web MJS/WASM pair is incomplete or uses the wrong namespace.",
|
|
728
942
|
undefined,
|
|
729
943
|
"artifact-graph-onnx-wasm-configuration-mismatch",
|
|
730
944
|
);
|
|
731
945
|
}
|
|
732
946
|
if (role === "tts" && wasm.numThreads !== undefined) {
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
947
|
+
throw workerError(
|
|
948
|
+
"ARCANE_AI_ARTIFACT_GRAPH_CONFIGURATION_INVALID",
|
|
949
|
+
"Kokoro does not expose a numThreads configuration field.",
|
|
736
950
|
undefined,
|
|
737
951
|
"kokoro-env-num-threads-field-not-exposed",
|
|
738
952
|
);
|
|
@@ -763,17 +977,6 @@ function validateConfiguration(configuration, role) {
|
|
|
763
977
|
);
|
|
764
978
|
}
|
|
765
979
|
}
|
|
766
|
-
if (
|
|
767
|
-
configuration.runtime.negativeRuntimeRequestUrls !== undefined
|
|
768
|
-
&& !Array.isArray(configuration.runtime.negativeRuntimeRequestUrls)
|
|
769
|
-
) {
|
|
770
|
-
throw workerError(
|
|
771
|
-
"ARCANE_AI_ARTIFACT_GRAPH_CONFIGURATION_INVALID",
|
|
772
|
-
"Artifact graph negative runtime request routes must be an array.",
|
|
773
|
-
undefined,
|
|
774
|
-
"artifact-graph-negative-request-routes-not-array",
|
|
775
|
-
);
|
|
776
|
-
}
|
|
777
980
|
}
|
|
778
981
|
return configuration;
|
|
779
982
|
}
|
|
@@ -794,6 +997,9 @@ function absoluteRequestUrl(value, scope) {
|
|
|
794
997
|
}
|
|
795
998
|
}
|
|
796
999
|
|
|
1000
|
+
// Dormant hardening retained for future review only. The closed route reader,
|
|
1001
|
+
// cache admission, and legacy fetch isolation below are not used by ordinary
|
|
1002
|
+
// execution and must not be enabled without explicit user review.
|
|
797
1003
|
function createArtifactRoutes(scope, configuration) {
|
|
798
1004
|
const positive = new Map();
|
|
799
1005
|
const negative = new Set();
|
|
@@ -833,7 +1039,7 @@ function createArtifactRoutes(scope, configuration) {
|
|
|
833
1039
|
}
|
|
834
1040
|
negative.add(absolute);
|
|
835
1041
|
}
|
|
836
|
-
return
|
|
1042
|
+
return completeValue({ files, positive, negative });
|
|
837
1043
|
}
|
|
838
1044
|
|
|
839
1045
|
function requestMethod(input, init, scope) {
|
|
@@ -951,16 +1157,16 @@ function createRouteReader(scope, configuration, originalFetch) {
|
|
|
951
1157
|
function resolve(input) {
|
|
952
1158
|
const absolute = absoluteRequestUrl(input, scope);
|
|
953
1159
|
const file = routes.positive.get(absolute);
|
|
954
|
-
if (file) return
|
|
1160
|
+
if (file) return completeValue({ kind: "file", absolute, file });
|
|
955
1161
|
if (routes.negative.has(absolute)) {
|
|
956
|
-
return
|
|
1162
|
+
return completeValue({ kind: "negative", absolute, file: null });
|
|
957
1163
|
}
|
|
958
1164
|
return null;
|
|
959
1165
|
}
|
|
960
1166
|
|
|
961
1167
|
function cacheForPaths(targetPaths) {
|
|
962
1168
|
const admittedPaths = new Set(targetPaths);
|
|
963
|
-
return
|
|
1169
|
+
return completeValue({
|
|
964
1170
|
async match(input) {
|
|
965
1171
|
const resolution = resolve(input);
|
|
966
1172
|
if (
|
|
@@ -1005,13 +1211,13 @@ function createRouteReader(scope, configuration, originalFetch) {
|
|
|
1005
1211
|
return false;
|
|
1006
1212
|
},
|
|
1007
1213
|
async keys() {
|
|
1008
|
-
return
|
|
1214
|
+
return completeValue([]);
|
|
1009
1215
|
},
|
|
1010
1216
|
});
|
|
1011
1217
|
}
|
|
1012
1218
|
|
|
1013
1219
|
const cache = cacheForPaths(routes.files.map((file) => file.path));
|
|
1014
|
-
return
|
|
1220
|
+
return completeValue({ cache, cacheForPaths, resolve, responseFor, routes });
|
|
1015
1221
|
}
|
|
1016
1222
|
|
|
1017
1223
|
function installLegacyAuthorizedFetch(scope, configuration) {
|
|
@@ -1051,11 +1257,11 @@ function installLegacyAuthorizedFetch(scope, configuration) {
|
|
|
1051
1257
|
authorized,
|
|
1052
1258
|
"speech-worker-fetch-isolation-unavailable",
|
|
1053
1259
|
);
|
|
1054
|
-
return
|
|
1260
|
+
return completeValue({ cache: null, cleanup: restore });
|
|
1055
1261
|
}
|
|
1056
1262
|
|
|
1057
1263
|
function installDeniedCacheIsolation(scope) {
|
|
1058
|
-
const denied =
|
|
1264
|
+
const denied = completeValue({
|
|
1059
1265
|
async open() {
|
|
1060
1266
|
throw workerError(
|
|
1061
1267
|
"ARCANE_AI_UNDECLARED_ARTIFACT",
|
|
@@ -1076,7 +1282,7 @@ function installDeniedCacheIsolation(scope) {
|
|
|
1076
1282
|
return false;
|
|
1077
1283
|
},
|
|
1078
1284
|
async keys() {
|
|
1079
|
-
return
|
|
1285
|
+
return completeValue([]);
|
|
1080
1286
|
},
|
|
1081
1287
|
async delete() {
|
|
1082
1288
|
return false;
|
|
@@ -1224,7 +1430,7 @@ function typedArrayIntrinsics(scope) {
|
|
|
1224
1430
|
"artifact-graph-typed-array-validation-unavailable",
|
|
1225
1431
|
);
|
|
1226
1432
|
}
|
|
1227
|
-
return
|
|
1433
|
+
return completeValue({
|
|
1228
1434
|
constructors,
|
|
1229
1435
|
dataViewPrototype: DataViewConstructor?.prototype ?? null,
|
|
1230
1436
|
getPrototypeOf: Object.getPrototypeOf,
|
|
@@ -1294,6 +1500,9 @@ function nestedWorkerFailureEvent(scope, error) {
|
|
|
1294
1500
|
return event;
|
|
1295
1501
|
}
|
|
1296
1502
|
|
|
1503
|
+
// Dormant hardening retained for future review only. Ordinary speech execution
|
|
1504
|
+
// never installs this guard, its declared-edge admission, or its isolation
|
|
1505
|
+
// controls. Do not enable it for secure mode without explicit user review.
|
|
1297
1506
|
function createArtifactGraphGuard(scope, configuration, role, reader, originalWorker) {
|
|
1298
1507
|
const files = runtimeFileMap(configuration);
|
|
1299
1508
|
const edges = configuration.runtime.edges;
|
|
@@ -1356,7 +1565,7 @@ function createArtifactGraphGuard(scope, configuration, role, reader, originalWo
|
|
|
1356
1565
|
return true;
|
|
1357
1566
|
}
|
|
1358
1567
|
|
|
1359
|
-
const guard =
|
|
1568
|
+
const guard = completeValue({
|
|
1360
1569
|
protocol: "arcane-ai-browser-speech-artifact-graph-runtime/1",
|
|
1361
1570
|
|
|
1362
1571
|
async dynamicImport(capability, modulePath, occurrence, specifier) {
|
|
@@ -1477,7 +1686,7 @@ function createArtifactGraphGuard(scope, configuration, role, reader, originalWo
|
|
|
1477
1686
|
event.stopImmediatePropagation?.();
|
|
1478
1687
|
const admitted = normalizeSpeechWorkerErrorEnvelope(event.data.error, role, "load");
|
|
1479
1688
|
const failure = admitted
|
|
1480
|
-
? workerError(admitted.code, admitted.message,
|
|
1689
|
+
? workerError(admitted.code, admitted.message, admitted.cause, admitted.reason)
|
|
1481
1690
|
: workerError(
|
|
1482
1691
|
"ARCANE_AI_WORKER_MESSAGE_ERROR",
|
|
1483
1692
|
"The authenticated artifact module Worker error envelope was rejected.",
|
|
@@ -1545,7 +1754,7 @@ function createArtifactGraphGuard(scope, configuration, role, reader, originalWo
|
|
|
1545
1754
|
},
|
|
1546
1755
|
});
|
|
1547
1756
|
|
|
1548
|
-
return
|
|
1757
|
+
return completeValue({
|
|
1549
1758
|
guard,
|
|
1550
1759
|
transformersCache: (() => {
|
|
1551
1760
|
const edgesForTransformers = [...cacheOpens.values()].filter((edge) =>
|
|
@@ -1640,7 +1849,6 @@ function installStringTimerIsolation(scope, name) {
|
|
|
1640
1849
|
function installArtifactGraphEnvironment(scope, configuration, role) {
|
|
1641
1850
|
const originalFetch = scope.fetch?.bind(scope);
|
|
1642
1851
|
const originalWorker = scope.Worker;
|
|
1643
|
-
const strictSecurity = configuration.security?.secure === true;
|
|
1644
1852
|
if (typeof originalFetch !== "function") {
|
|
1645
1853
|
throw workerError(
|
|
1646
1854
|
"ARCANE_AI_PROVIDER_UNAVAILABLE",
|
|
@@ -1667,120 +1875,18 @@ function installArtifactGraphEnvironment(scope, configuration, role) {
|
|
|
1667
1875
|
);
|
|
1668
1876
|
const restores = [];
|
|
1669
1877
|
try {
|
|
1670
|
-
if (strictSecurity) {
|
|
1671
|
-
restores.push(installDynamicCodeConstructorIsolation());
|
|
1672
|
-
restores.push(installStringTimerIsolation(scope, "setInterval"));
|
|
1673
|
-
restores.push(installStringTimerIsolation(scope, "setTimeout"));
|
|
1674
|
-
if ("indexedDB" in scope) {
|
|
1675
|
-
restores.push(installScopeValue(
|
|
1676
|
-
scope,
|
|
1677
|
-
"indexedDB",
|
|
1678
|
-
undefined,
|
|
1679
|
-
"artifact-graph-indexeddb-isolation-unavailable",
|
|
1680
|
-
));
|
|
1681
|
-
}
|
|
1682
|
-
if (scope.navigator && "storage" in scope.navigator) {
|
|
1683
|
-
restores.push(installObjectValue(
|
|
1684
|
-
scope.navigator,
|
|
1685
|
-
"storage",
|
|
1686
|
-
undefined,
|
|
1687
|
-
"WorkerNavigator.storage",
|
|
1688
|
-
"artifact-graph-opfs-isolation-unavailable",
|
|
1689
|
-
));
|
|
1690
|
-
}
|
|
1691
|
-
}
|
|
1692
1878
|
restores.push(installScopeValue(
|
|
1693
1879
|
scope,
|
|
1694
1880
|
GRAPH_GUARD_NAME,
|
|
1695
1881
|
graphGuard.guard,
|
|
1696
1882
|
"artifact-graph-guard-global-definition-rejected",
|
|
1697
1883
|
));
|
|
1698
|
-
if (strictSecurity) {
|
|
1699
|
-
restores.push(installScopeValue(
|
|
1700
|
-
scope,
|
|
1701
|
-
"fetch",
|
|
1702
|
-
async function rejectUntransformedArtifactGraphFetch() {
|
|
1703
|
-
throw workerError(
|
|
1704
|
-
"ARCANE_AI_ARTIFACT_GRAPH_FETCH_EDGE_UNDECLARED",
|
|
1705
|
-
"Artifact graph fetch must use its declared transformed edge.",
|
|
1706
|
-
undefined,
|
|
1707
|
-
"artifact-graph-fetch-guard-bypassed",
|
|
1708
|
-
);
|
|
1709
|
-
},
|
|
1710
|
-
"artifact-graph-fetch-isolation-unavailable",
|
|
1711
|
-
));
|
|
1712
|
-
const cacheStorage = Object.freeze({
|
|
1713
|
-
async open() {
|
|
1714
|
-
throw workerError(
|
|
1715
|
-
"ARCANE_AI_ARTIFACT_GRAPH_CACHE_EDGE_UNDECLARED",
|
|
1716
|
-
"Artifact graph CacheStorage.open must use its declared transformed edge.",
|
|
1717
|
-
undefined,
|
|
1718
|
-
"artifact-graph-cache-open-guard-bypassed",
|
|
1719
|
-
);
|
|
1720
|
-
},
|
|
1721
|
-
async match() {
|
|
1722
|
-
throw workerError(
|
|
1723
|
-
"ARCANE_AI_ARTIFACT_GRAPH_CACHE_EDGE_UNDECLARED",
|
|
1724
|
-
"Artifact graph CacheStorage.match requires a declared cache-open edge.",
|
|
1725
|
-
undefined,
|
|
1726
|
-
"artifact-graph-cache-match-guard-bypassed",
|
|
1727
|
-
);
|
|
1728
|
-
},
|
|
1729
|
-
async has() {
|
|
1730
|
-
return true;
|
|
1731
|
-
},
|
|
1732
|
-
async keys() {
|
|
1733
|
-
return Object.freeze([]);
|
|
1734
|
-
},
|
|
1735
|
-
async delete() {
|
|
1736
|
-
return false;
|
|
1737
|
-
},
|
|
1738
|
-
});
|
|
1739
|
-
restores.push(installScopeValue(
|
|
1740
|
-
scope,
|
|
1741
|
-
"caches",
|
|
1742
|
-
cacheStorage,
|
|
1743
|
-
"artifact-graph-cache-isolation-unavailable",
|
|
1744
|
-
));
|
|
1745
|
-
|
|
1746
|
-
const deniedCapabilities = [
|
|
1747
|
-
"BroadcastChannel",
|
|
1748
|
-
"EventSource",
|
|
1749
|
-
"Function",
|
|
1750
|
-
"RTCPeerConnection",
|
|
1751
|
-
"ShadowRealm",
|
|
1752
|
-
"SharedWorker",
|
|
1753
|
-
"WebSocket",
|
|
1754
|
-
"WebSocketStream",
|
|
1755
|
-
"WebTransport",
|
|
1756
|
-
"Worker",
|
|
1757
|
-
"XMLHttpRequest",
|
|
1758
|
-
"eval",
|
|
1759
|
-
"importScripts",
|
|
1760
|
-
];
|
|
1761
|
-
for (const name of deniedCapabilities) {
|
|
1762
|
-
if (!(name in scope)) continue;
|
|
1763
|
-
restores.push(installScopeValue(
|
|
1764
|
-
scope,
|
|
1765
|
-
name,
|
|
1766
|
-
function rejectUndeclaredArtifactGraphCapability() {
|
|
1767
|
-
throw workerError(
|
|
1768
|
-
"ARCANE_AI_ARTIFACT_GRAPH_ISOLATION_UNAVAILABLE",
|
|
1769
|
-
`The authenticated artifact graph denied raw ${name} access.`,
|
|
1770
|
-
undefined,
|
|
1771
|
-
`artifact-graph-${name.toLowerCase()}-capability-undeclared`,
|
|
1772
|
-
);
|
|
1773
|
-
},
|
|
1774
|
-
`artifact-graph-${name.toLowerCase()}-isolation-unavailable`,
|
|
1775
|
-
));
|
|
1776
|
-
}
|
|
1777
|
-
}
|
|
1778
1884
|
} catch (error) {
|
|
1779
1885
|
restoreScopeValues(restores);
|
|
1780
1886
|
graphGuard.cleanup();
|
|
1781
1887
|
throw error;
|
|
1782
1888
|
}
|
|
1783
|
-
return
|
|
1889
|
+
return completeValue({
|
|
1784
1890
|
cache: graphGuard.transformersCache,
|
|
1785
1891
|
cleanup() {
|
|
1786
1892
|
graphGuard.cleanup();
|
|
@@ -1789,6 +1895,300 @@ function installArtifactGraphEnvironment(scope, configuration, role) {
|
|
|
1789
1895
|
});
|
|
1790
1896
|
}
|
|
1791
1897
|
|
|
1898
|
+
function ordinaryRequestUrl(value, scope, base = scope.location?.href) {
|
|
1899
|
+
const input = typeof value === "string" || value instanceof URL
|
|
1900
|
+
? String(value)
|
|
1901
|
+
: value?.url;
|
|
1902
|
+
if (typeof input !== "string" || input.length < 1) return null;
|
|
1903
|
+
try {
|
|
1904
|
+
return new URL(input, base).href;
|
|
1905
|
+
} catch {
|
|
1906
|
+
return null;
|
|
1907
|
+
}
|
|
1908
|
+
}
|
|
1909
|
+
|
|
1910
|
+
function isBareModuleSpecifier(value) {
|
|
1911
|
+
return typeof value === "string"
|
|
1912
|
+
&& !value.startsWith("./")
|
|
1913
|
+
&& !value.startsWith("../")
|
|
1914
|
+
&& !value.startsWith("/")
|
|
1915
|
+
&& !/^[A-Za-z][A-Za-z\d+.-]*:/u.test(value);
|
|
1916
|
+
}
|
|
1917
|
+
|
|
1918
|
+
function createOrdinaryRoutes(scope, configuration) {
|
|
1919
|
+
const files = [
|
|
1920
|
+
...configuration.runtime.files,
|
|
1921
|
+
...configuration.model.files,
|
|
1922
|
+
];
|
|
1923
|
+
const filesByPath = new Map(files.map((file) => [file.path, file]));
|
|
1924
|
+
const routes = new Map();
|
|
1925
|
+
const ambiguous = new Set();
|
|
1926
|
+
|
|
1927
|
+
function add(route, file) {
|
|
1928
|
+
const absolute = ordinaryRequestUrl(route, scope);
|
|
1929
|
+
if (!absolute || ambiguous.has(absolute)) return;
|
|
1930
|
+
const existing = routes.get(absolute);
|
|
1931
|
+
if (existing && existing.path !== file.path) {
|
|
1932
|
+
routes.delete(absolute);
|
|
1933
|
+
ambiguous.add(absolute);
|
|
1934
|
+
return;
|
|
1935
|
+
}
|
|
1936
|
+
routes.set(absolute, file);
|
|
1937
|
+
}
|
|
1938
|
+
|
|
1939
|
+
for (const file of files) {
|
|
1940
|
+
add(file.sourceUrl, file);
|
|
1941
|
+
add(file.moduleUrl, file);
|
|
1942
|
+
for (const route of file.runtimeRequestUrls ?? []) add(route, file);
|
|
1943
|
+
}
|
|
1944
|
+
return completeValue({ files, filesByPath, routes });
|
|
1945
|
+
}
|
|
1946
|
+
|
|
1947
|
+
function createOrdinaryRouteReader(
|
|
1948
|
+
scope,
|
|
1949
|
+
configuration,
|
|
1950
|
+
originalFetch,
|
|
1951
|
+
originalCaches,
|
|
1952
|
+
) {
|
|
1953
|
+
const routeTable = createOrdinaryRoutes(scope, configuration);
|
|
1954
|
+
const nativeCaches = new Map();
|
|
1955
|
+
const RequestConstructor = scope.Request ?? globalThis.Request;
|
|
1956
|
+
|
|
1957
|
+
function sourceBase(modulePath) {
|
|
1958
|
+
return routeTable.filesByPath.get(modulePath)?.sourceUrl ?? scope.location?.href;
|
|
1959
|
+
}
|
|
1960
|
+
|
|
1961
|
+
function nativeInput(input, modulePath) {
|
|
1962
|
+
if (typeof input !== "string" && !(input instanceof URL)) return input;
|
|
1963
|
+
return ordinaryRequestUrl(input, scope, sourceBase(modulePath)) ?? input;
|
|
1964
|
+
}
|
|
1965
|
+
|
|
1966
|
+
function resolve(input, modulePath) {
|
|
1967
|
+
const absolute = ordinaryRequestUrl(input, scope, sourceBase(modulePath));
|
|
1968
|
+
const file = absolute ? routeTable.routes.get(absolute) : null;
|
|
1969
|
+
return file ? completeValue({ absolute, file }) : null;
|
|
1970
|
+
}
|
|
1971
|
+
|
|
1972
|
+
function responseFor(resolution, input, init) {
|
|
1973
|
+
const mappedInput = typeof RequestConstructor === "function"
|
|
1974
|
+
&& input instanceof RequestConstructor
|
|
1975
|
+
? new RequestConstructor(resolution.file.moduleUrl, input)
|
|
1976
|
+
: resolution.file.moduleUrl;
|
|
1977
|
+
return originalFetch(mappedInput, init);
|
|
1978
|
+
}
|
|
1979
|
+
|
|
1980
|
+
async function nativeCache(name) {
|
|
1981
|
+
if (!originalCaches || typeof originalCaches.open !== "function") return null;
|
|
1982
|
+
if (!nativeCaches.has(name)) {
|
|
1983
|
+
nativeCaches.set(name, Promise.resolve(originalCaches.open.call(originalCaches, name)));
|
|
1984
|
+
}
|
|
1985
|
+
return nativeCaches.get(name);
|
|
1986
|
+
}
|
|
1987
|
+
|
|
1988
|
+
function cacheForName(name, modulePath) {
|
|
1989
|
+
return completeValue({
|
|
1990
|
+
async match(input, options) {
|
|
1991
|
+
const resolution = resolve(input, modulePath);
|
|
1992
|
+
if (resolution) return responseFor(resolution, input);
|
|
1993
|
+
const cache = await nativeCache(name);
|
|
1994
|
+
return cache?.match(nativeInput(input, modulePath), options);
|
|
1995
|
+
},
|
|
1996
|
+
async put(input, response) {
|
|
1997
|
+
const cache = await nativeCache(name);
|
|
1998
|
+
if (!cache) throw new TypeError("Browser CacheStorage is unavailable.");
|
|
1999
|
+
return cache.put(nativeInput(input, modulePath), response);
|
|
2000
|
+
},
|
|
2001
|
+
async add(input) {
|
|
2002
|
+
const cache = await nativeCache(name);
|
|
2003
|
+
if (!cache) throw new TypeError("Browser CacheStorage is unavailable.");
|
|
2004
|
+
return cache.add(nativeInput(input, modulePath));
|
|
2005
|
+
},
|
|
2006
|
+
async addAll(inputs) {
|
|
2007
|
+
const cache = await nativeCache(name);
|
|
2008
|
+
if (!cache) throw new TypeError("Browser CacheStorage is unavailable.");
|
|
2009
|
+
return cache.addAll(Array.from(inputs, (input) => nativeInput(input, modulePath)));
|
|
2010
|
+
},
|
|
2011
|
+
async delete(input, options) {
|
|
2012
|
+
const cache = await nativeCache(name);
|
|
2013
|
+
return cache ? cache.delete(nativeInput(input, modulePath), options) : false;
|
|
2014
|
+
},
|
|
2015
|
+
async keys(input, options) {
|
|
2016
|
+
const cache = await nativeCache(name);
|
|
2017
|
+
if (!cache) return [];
|
|
2018
|
+
return input === undefined
|
|
2019
|
+
? cache.keys()
|
|
2020
|
+
: cache.keys(nativeInput(input, modulePath), options);
|
|
2021
|
+
},
|
|
2022
|
+
});
|
|
2023
|
+
}
|
|
2024
|
+
|
|
2025
|
+
return completeValue({ cacheForName, nativeInput, resolve, responseFor });
|
|
2026
|
+
}
|
|
2027
|
+
|
|
2028
|
+
function createOrdinaryArtifactModuleRouter(
|
|
2029
|
+
scope,
|
|
2030
|
+
configuration,
|
|
2031
|
+
role,
|
|
2032
|
+
reader,
|
|
2033
|
+
originalFetch,
|
|
2034
|
+
originalWorker,
|
|
2035
|
+
) {
|
|
2036
|
+
const nestedWorkers = new Set();
|
|
2037
|
+
const router = completeValue({
|
|
2038
|
+
async dynamicImport(modulePath, specifier) {
|
|
2039
|
+
const resolution = isBareModuleSpecifier(specifier)
|
|
2040
|
+
? null
|
|
2041
|
+
: reader.resolve(specifier, modulePath);
|
|
2042
|
+
const target = resolution?.file.moduleUrl
|
|
2043
|
+
?? (isBareModuleSpecifier(specifier)
|
|
2044
|
+
? specifier
|
|
2045
|
+
: reader.nativeInput(specifier, modulePath));
|
|
2046
|
+
return import(target);
|
|
2047
|
+
},
|
|
2048
|
+
|
|
2049
|
+
fetch(modulePath, input, init) {
|
|
2050
|
+
const resolution = reader.resolve(input, modulePath);
|
|
2051
|
+
return resolution
|
|
2052
|
+
? reader.responseFor(resolution, input, init)
|
|
2053
|
+
: originalFetch(reader.nativeInput(input, modulePath), init);
|
|
2054
|
+
},
|
|
2055
|
+
|
|
2056
|
+
openCache(modulePath, name) {
|
|
2057
|
+
return Promise.resolve(reader.cacheForName(name, modulePath));
|
|
2058
|
+
},
|
|
2059
|
+
|
|
2060
|
+
createWorker(modulePath, specifier, options = {}) {
|
|
2061
|
+
if (typeof originalWorker !== "function") {
|
|
2062
|
+
throw workerError(
|
|
2063
|
+
"ARCANE_AI_PROVIDER_UNAVAILABLE",
|
|
2064
|
+
"Nested browser Workers are unavailable.",
|
|
2065
|
+
undefined,
|
|
2066
|
+
"speech-module-worker-constructor-unavailable",
|
|
2067
|
+
);
|
|
2068
|
+
}
|
|
2069
|
+
const resolution = reader.resolve(specifier, modulePath);
|
|
2070
|
+
if (!resolution) {
|
|
2071
|
+
return new originalWorker(reader.nativeInput(specifier, modulePath), options);
|
|
2072
|
+
}
|
|
2073
|
+
const workerOptions = options && typeof options === "object"
|
|
2074
|
+
? { ...options, type: "module" }
|
|
2075
|
+
: { type: "module" };
|
|
2076
|
+
const worker = new originalWorker(nestedWorkerUrl(role), workerOptions);
|
|
2077
|
+
nestedWorkers.add(worker);
|
|
2078
|
+
const onBootstrapMessage = (event) => {
|
|
2079
|
+
if (event.data?.protocol !== NESTED_WORKER_PROTOCOL
|
|
2080
|
+
|| event.data?.event !== "artifact-module-worker-bootstrap-rejected") return;
|
|
2081
|
+
event.stopImmediatePropagation?.();
|
|
2082
|
+
const admitted = normalizeSpeechWorkerErrorEnvelope(event.data.error, role, "load");
|
|
2083
|
+
const failure = admitted
|
|
2084
|
+
? workerError(admitted.code, admitted.message, admitted.cause, admitted.reason)
|
|
2085
|
+
: workerError(
|
|
2086
|
+
"ARCANE_AI_WORKER_MESSAGE_ERROR",
|
|
2087
|
+
"The artifact module Worker error envelope was rejected.",
|
|
2088
|
+
undefined,
|
|
2089
|
+
"speech-module-worker-error-envelope-rejected",
|
|
2090
|
+
);
|
|
2091
|
+
worker.dispatchEvent?.(nestedWorkerFailureEvent(scope, failure));
|
|
2092
|
+
};
|
|
2093
|
+
worker.addEventListener?.("message", onBootstrapMessage);
|
|
2094
|
+
try {
|
|
2095
|
+
worker.postMessage({
|
|
2096
|
+
protocol: NESTED_WORKER_PROTOCOL,
|
|
2097
|
+
op: "initialize-artifact-module-worker",
|
|
2098
|
+
role,
|
|
2099
|
+
targetPath: resolution.file.path,
|
|
2100
|
+
configuration,
|
|
2101
|
+
});
|
|
2102
|
+
} catch (error) {
|
|
2103
|
+
nestedWorkers.delete(worker);
|
|
2104
|
+
worker.terminate();
|
|
2105
|
+
throw workerError(
|
|
2106
|
+
"ARCANE_AI_WORKER_MESSAGE_ERROR",
|
|
2107
|
+
"The artifact module Worker initialization message was rejected.",
|
|
2108
|
+
error,
|
|
2109
|
+
"speech-module-worker-initialization-message-rejected",
|
|
2110
|
+
);
|
|
2111
|
+
}
|
|
2112
|
+
return worker;
|
|
2113
|
+
},
|
|
2114
|
+
});
|
|
2115
|
+
return completeValue({
|
|
2116
|
+
router,
|
|
2117
|
+
transformersCache: reader.cacheForName("transformers-cache", null),
|
|
2118
|
+
cleanup() {
|
|
2119
|
+
for (const worker of nestedWorkers) {
|
|
2120
|
+
try {
|
|
2121
|
+
worker.terminate();
|
|
2122
|
+
} catch {
|
|
2123
|
+
// The owning speech Worker is already being torn down.
|
|
2124
|
+
}
|
|
2125
|
+
}
|
|
2126
|
+
nestedWorkers.clear();
|
|
2127
|
+
},
|
|
2128
|
+
});
|
|
2129
|
+
}
|
|
2130
|
+
|
|
2131
|
+
function installOrdinaryArtifactModuleRouter(scope, configuration, role) {
|
|
2132
|
+
const originalFetch = scope.fetch?.bind(scope);
|
|
2133
|
+
const originalWorker = scope.Worker;
|
|
2134
|
+
const originalCaches = scope.caches;
|
|
2135
|
+
if (typeof originalFetch !== "function") {
|
|
2136
|
+
throw workerError(
|
|
2137
|
+
"ARCANE_AI_PROVIDER_UNAVAILABLE",
|
|
2138
|
+
"Browser fetch is unavailable in the speech Worker.",
|
|
2139
|
+
undefined,
|
|
2140
|
+
"speech-worker-fetch-unavailable",
|
|
2141
|
+
);
|
|
2142
|
+
}
|
|
2143
|
+
const reader = createOrdinaryRouteReader(
|
|
2144
|
+
scope,
|
|
2145
|
+
configuration,
|
|
2146
|
+
originalFetch,
|
|
2147
|
+
originalCaches,
|
|
2148
|
+
);
|
|
2149
|
+
const moduleRouter = createOrdinaryArtifactModuleRouter(
|
|
2150
|
+
scope,
|
|
2151
|
+
configuration,
|
|
2152
|
+
role,
|
|
2153
|
+
reader,
|
|
2154
|
+
originalFetch,
|
|
2155
|
+
originalWorker,
|
|
2156
|
+
);
|
|
2157
|
+
const hadOwn = Object.prototype.hasOwnProperty.call(scope, MODULE_ROUTER_NAME);
|
|
2158
|
+
const previous = scope[MODULE_ROUTER_NAME];
|
|
2159
|
+
try {
|
|
2160
|
+
scope[MODULE_ROUTER_NAME] = moduleRouter.router;
|
|
2161
|
+
if (scope[MODULE_ROUTER_NAME] !== moduleRouter.router) {
|
|
2162
|
+
Object.defineProperty(scope, MODULE_ROUTER_NAME, {
|
|
2163
|
+
configurable: true,
|
|
2164
|
+
enumerable: true,
|
|
2165
|
+
writable: true,
|
|
2166
|
+
value: moduleRouter.router,
|
|
2167
|
+
});
|
|
2168
|
+
}
|
|
2169
|
+
} catch (error) {
|
|
2170
|
+
moduleRouter.cleanup();
|
|
2171
|
+
throw workerError(
|
|
2172
|
+
"ARCANE_AI_PROVIDER_UNAVAILABLE",
|
|
2173
|
+
"The speech Worker cannot install the artifact module router.",
|
|
2174
|
+
error,
|
|
2175
|
+
"speech-module-router-unavailable",
|
|
2176
|
+
);
|
|
2177
|
+
}
|
|
2178
|
+
return completeValue({
|
|
2179
|
+
cache: moduleRouter.transformersCache,
|
|
2180
|
+
cleanup() {
|
|
2181
|
+
moduleRouter.cleanup();
|
|
2182
|
+
try {
|
|
2183
|
+
if (hadOwn) scope[MODULE_ROUTER_NAME] = previous;
|
|
2184
|
+
else delete scope[MODULE_ROUTER_NAME];
|
|
2185
|
+
} catch {
|
|
2186
|
+
// Worker teardown releases the remaining module router reference.
|
|
2187
|
+
}
|
|
2188
|
+
},
|
|
2189
|
+
});
|
|
2190
|
+
}
|
|
2191
|
+
|
|
1792
2192
|
function assignSetting(object, name, value, cleanup, {
|
|
1793
2193
|
allowCreate = false,
|
|
1794
2194
|
assignmentRejectedReason,
|
|
@@ -1846,14 +2246,6 @@ function assignSetting(object, name, value, cleanup, {
|
|
|
1846
2246
|
|
|
1847
2247
|
function configuredWasmPaths(configuration) {
|
|
1848
2248
|
if (configuration.runtime.wasmPaths !== undefined) {
|
|
1849
|
-
if (configuration.security?.secure === true) {
|
|
1850
|
-
throw workerError(
|
|
1851
|
-
"ARCANE_AI_INVALID_REQUEST",
|
|
1852
|
-
"Secure browser speech cannot use remote wasmPaths.",
|
|
1853
|
-
undefined,
|
|
1854
|
-
"speech-worker-secure-remote-wasm-paths-rejected",
|
|
1855
|
-
);
|
|
1856
|
-
}
|
|
1857
2249
|
return configuration.runtime.wasmPaths;
|
|
1858
2250
|
}
|
|
1859
2251
|
const byPath = new Map(configuration.runtime.files.map((file) => [file.path, file]));
|
|
@@ -1874,12 +2266,11 @@ function configuredWasmPaths(configuration) {
|
|
|
1874
2266
|
"artifact-graph-onnx-wasm-pair-not-materialized",
|
|
1875
2267
|
);
|
|
1876
2268
|
}
|
|
1877
|
-
return
|
|
2269
|
+
return completeValue({ mjs: mjs.moduleUrl, wasm: wasm.moduleUrl });
|
|
1878
2270
|
}
|
|
1879
2271
|
|
|
1880
2272
|
function configureRuntimeNamespace(namespace, configuration, role, cache) {
|
|
1881
2273
|
const cleanup = [];
|
|
1882
|
-
const strictSecurity = configuration.security?.secure === true;
|
|
1883
2274
|
const restoreSettings = () => {
|
|
1884
2275
|
for (const restore of cleanup.splice(0).reverse()) {
|
|
1885
2276
|
try {
|
|
@@ -1939,24 +2330,6 @@ function configureRuntimeNamespace(namespace, configuration, role, cache) {
|
|
|
1939
2330
|
assignmentRejectedReason: "transformers-env-allow-remote-models-assignment-rejected",
|
|
1940
2331
|
unavailableReason: "transformers-env-allow-remote-models-unavailable",
|
|
1941
2332
|
});
|
|
1942
|
-
if (strictSecurity) {
|
|
1943
|
-
assignSetting(env, "useBrowserCache", false, cleanup, {
|
|
1944
|
-
assignmentRejectedReason: "transformers-env-browser-cache-assignment-rejected",
|
|
1945
|
-
unavailableReason: "transformers-env-browser-cache-unavailable",
|
|
1946
|
-
});
|
|
1947
|
-
assignSetting(env, "useFSCache", false, cleanup, {
|
|
1948
|
-
assignmentRejectedReason: "transformers-env-fs-cache-assignment-rejected",
|
|
1949
|
-
unavailableReason: "transformers-env-fs-cache-unavailable",
|
|
1950
|
-
});
|
|
1951
|
-
assignSetting(env, "useCustomCache", cache !== null, cleanup, {
|
|
1952
|
-
assignmentRejectedReason: "transformers-env-custom-cache-toggle-assignment-rejected",
|
|
1953
|
-
unavailableReason: "transformers-env-custom-cache-toggle-unavailable",
|
|
1954
|
-
});
|
|
1955
|
-
assignSetting(env, "customCache", cache, cleanup, {
|
|
1956
|
-
assignmentRejectedReason: "transformers-env-custom-cache-assignment-rejected",
|
|
1957
|
-
unavailableReason: "transformers-env-custom-cache-unavailable",
|
|
1958
|
-
});
|
|
1959
|
-
}
|
|
1960
2333
|
if (paths) {
|
|
1961
2334
|
assignSetting(
|
|
1962
2335
|
wasm,
|
|
@@ -1990,39 +2363,6 @@ function configureRuntimeNamespace(namespace, configuration, role, cache) {
|
|
|
1990
2363
|
return restoreSettings;
|
|
1991
2364
|
}
|
|
1992
2365
|
|
|
1993
|
-
function workerProgress(send, role, requestId, phase, completed = 0, total = null, unit = "items") {
|
|
1994
|
-
send({
|
|
1995
|
-
protocol: SPEECH_WORKER_PROTOCOL,
|
|
1996
|
-
event: "progress",
|
|
1997
|
-
requestId,
|
|
1998
|
-
progress: Object.freeze({
|
|
1999
|
-
phase,
|
|
2000
|
-
completed,
|
|
2001
|
-
total,
|
|
2002
|
-
unit,
|
|
2003
|
-
heartbeat: true,
|
|
2004
|
-
}),
|
|
2005
|
-
}, []);
|
|
2006
|
-
}
|
|
2007
|
-
|
|
2008
|
-
function upstreamProgress(send, role, requestId) {
|
|
2009
|
-
return function reportSpeechModelProgress(update = {}) {
|
|
2010
|
-
const completed = Number.isFinite(update.loaded)
|
|
2011
|
-
? update.loaded
|
|
2012
|
-
: Number.isFinite(update.progress) ? update.progress : 0;
|
|
2013
|
-
const total = Number.isFinite(update.total) ? update.total : null;
|
|
2014
|
-
workerProgress(
|
|
2015
|
-
send,
|
|
2016
|
-
role,
|
|
2017
|
-
requestId,
|
|
2018
|
-
`${role}-model-load-progress`,
|
|
2019
|
-
completed,
|
|
2020
|
-
total,
|
|
2021
|
-
Number.isFinite(update.loaded) ? "bytes" : "items",
|
|
2022
|
-
);
|
|
2023
|
-
};
|
|
2024
|
-
}
|
|
2025
|
-
|
|
2026
2366
|
async function disposeEngine(engine) {
|
|
2027
2367
|
if (!engine) return;
|
|
2028
2368
|
if (typeof engine.dispose === "function") {
|
|
@@ -2067,12 +2407,12 @@ async function createWhisperEngine(namespace, configuration, signal, report) {
|
|
|
2067
2407
|
}
|
|
2068
2408
|
throw error;
|
|
2069
2409
|
}
|
|
2070
|
-
return
|
|
2410
|
+
return completeValue({
|
|
2071
2411
|
async transcribe(input, { signal: requestSignal } = {}) {
|
|
2072
2412
|
throwIfAborted(requestSignal, "stt-transcription-cancelled");
|
|
2073
2413
|
const output = await transcriber(input.audio, { signal: requestSignal });
|
|
2074
2414
|
throwIfAborted(requestSignal, "stt-transcription-cancelled");
|
|
2075
|
-
return
|
|
2415
|
+
return completeValue({ text: String(output?.text ?? "") });
|
|
2076
2416
|
},
|
|
2077
2417
|
dispose: () => disposeEngine(transcriber),
|
|
2078
2418
|
});
|
|
@@ -2107,7 +2447,7 @@ async function createKokoroEngine(namespace, configuration, signal, report) {
|
|
|
2107
2447
|
}
|
|
2108
2448
|
throw error;
|
|
2109
2449
|
}
|
|
2110
|
-
return
|
|
2450
|
+
return completeValue({
|
|
2111
2451
|
async synthesize(input, { signal: requestSignal } = {}) {
|
|
2112
2452
|
throwIfAborted(requestSignal, "tts-synthesis-cancelled");
|
|
2113
2453
|
const output = await synthesizer.generate(input.text, {
|
|
@@ -2119,7 +2459,7 @@ async function createKokoroEngine(namespace, configuration, signal, report) {
|
|
|
2119
2459
|
const audio = output?.audio instanceof Float32Array
|
|
2120
2460
|
? output.audio
|
|
2121
2461
|
: new Float32Array(output?.audio ?? []);
|
|
2122
|
-
return
|
|
2462
|
+
return completeValue({
|
|
2123
2463
|
audio,
|
|
2124
2464
|
sampleRate: output?.sampling_rate,
|
|
2125
2465
|
voice: input.voice,
|
|
@@ -2157,9 +2497,9 @@ function validateInput(role, payload, configuration) {
|
|
|
2157
2497
|
"stt-transcription-sample-rate-mismatch",
|
|
2158
2498
|
);
|
|
2159
2499
|
}
|
|
2160
|
-
return
|
|
2500
|
+
return completeValue({ audio: payload.audio, sampleRate: inputSampleRate });
|
|
2161
2501
|
}
|
|
2162
|
-
const text =
|
|
2502
|
+
const text = requiredContent(payload?.text, "Kokoro text", "tts-synthesis-text-empty");
|
|
2163
2503
|
const voice = requiredText(
|
|
2164
2504
|
payload?.voice ?? configuration.model.defaultVoice,
|
|
2165
2505
|
"Kokoro voice",
|
|
@@ -2175,15 +2515,15 @@ function validateInput(role, payload, configuration) {
|
|
|
2175
2515
|
);
|
|
2176
2516
|
}
|
|
2177
2517
|
const speed = payload?.speed ?? 1;
|
|
2178
|
-
if (!Number.isFinite(speed) || speed <= 0
|
|
2518
|
+
if (!Number.isFinite(speed) || speed <= 0) {
|
|
2179
2519
|
throw workerError(
|
|
2180
2520
|
"ARCANE_AI_INVALID_REQUEST",
|
|
2181
|
-
"Kokoro speed must be greater than 0
|
|
2521
|
+
"Kokoro speed must be greater than 0.",
|
|
2182
2522
|
undefined,
|
|
2183
|
-
"tts-synthesis-speed-
|
|
2523
|
+
"tts-synthesis-speed-not-positive",
|
|
2184
2524
|
);
|
|
2185
2525
|
}
|
|
2186
|
-
return
|
|
2526
|
+
return completeValue({ text, voice, speed });
|
|
2187
2527
|
}
|
|
2188
2528
|
|
|
2189
2529
|
function validateResult(role, result, configuration) {
|
|
@@ -2196,7 +2536,7 @@ function validateResult(role, result, configuration) {
|
|
|
2196
2536
|
"stt-transcription-result-text-not-string",
|
|
2197
2537
|
);
|
|
2198
2538
|
}
|
|
2199
|
-
return
|
|
2539
|
+
return completeValue({ text: result.text });
|
|
2200
2540
|
}
|
|
2201
2541
|
const outputSampleRate = requiredSampleRate(
|
|
2202
2542
|
configuration.model.outputSampleRate,
|
|
@@ -2229,7 +2569,7 @@ function validateResult(role, result, configuration) {
|
|
|
2229
2569
|
);
|
|
2230
2570
|
}
|
|
2231
2571
|
}
|
|
2232
|
-
return
|
|
2572
|
+
return completeValue({
|
|
2233
2573
|
audio: result.audio,
|
|
2234
2574
|
sampleRate: outputSampleRate,
|
|
2235
2575
|
voice: result.voice,
|
|
@@ -2254,16 +2594,7 @@ export function createSpeechWorkerRuntime({ role, scope = globalThis, send } = {
|
|
|
2254
2594
|
|
|
2255
2595
|
function status() {
|
|
2256
2596
|
const state = disposed ? "disposed" : engine ? "ready" : "unloaded";
|
|
2257
|
-
|
|
2258
|
-
? Object.freeze({
|
|
2259
|
-
secure: configuration.security.secure === true,
|
|
2260
|
-
checks: Object.freeze({
|
|
2261
|
-
byteLength: configuration.security.checks?.byteLength === true,
|
|
2262
|
-
sha256: configuration.security.checks?.sha256 === true,
|
|
2263
|
-
}),
|
|
2264
|
-
})
|
|
2265
|
-
: null;
|
|
2266
|
-
return Object.freeze({
|
|
2597
|
+
return completeValue({
|
|
2267
2598
|
state,
|
|
2268
2599
|
lifecycleStatus: `${role}-worker-${state}`,
|
|
2269
2600
|
lifecycleReason,
|
|
@@ -2271,9 +2602,6 @@ export function createSpeechWorkerRuntime({ role, scope = globalThis, send } = {
|
|
|
2271
2602
|
loaded: engine !== null,
|
|
2272
2603
|
busy: operations.size > 0,
|
|
2273
2604
|
activeOperation: operations.values().next().value?.publicOperation ?? null,
|
|
2274
|
-
security,
|
|
2275
|
-
artifactGraphId: configuration?.runtime?.artifactGraphId ?? null,
|
|
2276
|
-
artifactGraphAdmission: configuration?.runtime?.artifactGraphAdmission ?? null,
|
|
2277
2605
|
});
|
|
2278
2606
|
}
|
|
2279
2607
|
|
|
@@ -2293,33 +2621,14 @@ export function createSpeechWorkerRuntime({ role, scope = globalThis, send } = {
|
|
|
2293
2621
|
configuration = validateConfiguration(request.payload?.configuration, role);
|
|
2294
2622
|
const entry = configuration.runtime.files.find((file) =>
|
|
2295
2623
|
file.path === configuration.runtime.entry);
|
|
2296
|
-
if (graphConfiguration(configuration)
|
|
2297
|
-
environment =
|
|
2298
|
-
} else if (configuration.security?.secure === true) {
|
|
2299
|
-
const legacyFetch = installLegacyAuthorizedFetch(scope, configuration);
|
|
2300
|
-
try {
|
|
2301
|
-
const restoreCaches = installDeniedCacheIsolation(scope);
|
|
2302
|
-
environment = Object.freeze({
|
|
2303
|
-
cache: null,
|
|
2304
|
-
cleanup() {
|
|
2305
|
-
try {
|
|
2306
|
-
restoreCaches();
|
|
2307
|
-
} finally {
|
|
2308
|
-
legacyFetch.cleanup();
|
|
2309
|
-
}
|
|
2310
|
-
},
|
|
2311
|
-
});
|
|
2312
|
-
} catch (error) {
|
|
2313
|
-
legacyFetch.cleanup();
|
|
2314
|
-
throw error;
|
|
2315
|
-
}
|
|
2624
|
+
if (graphConfiguration(configuration)) {
|
|
2625
|
+
environment = installOrdinaryArtifactModuleRouter(scope, configuration, role);
|
|
2316
2626
|
} else {
|
|
2317
|
-
environment =
|
|
2627
|
+
environment = completeValue({
|
|
2318
2628
|
cache: null,
|
|
2319
2629
|
cleanup() {},
|
|
2320
2630
|
});
|
|
2321
2631
|
}
|
|
2322
|
-
workerProgress(send, role, request.id, `${role}-runtime-import-started`);
|
|
2323
2632
|
loadFailureReason = `${role}-worker-runtime-import-rejected`;
|
|
2324
2633
|
const namespace = await import(entry.moduleUrl);
|
|
2325
2634
|
throwIfAborted(signal, `${role}-load-cancelled`);
|
|
@@ -2329,14 +2638,12 @@ export function createSpeechWorkerRuntime({ role, scope = globalThis, send } = {
|
|
|
2329
2638
|
role,
|
|
2330
2639
|
environment.cache,
|
|
2331
2640
|
);
|
|
2332
|
-
workerProgress(send, role, request.id, `${role}-model-load-started`);
|
|
2333
2641
|
loadFailureReason = `${role}-worker-model-load-rejected`;
|
|
2334
|
-
const report =
|
|
2642
|
+
const report = () => undefined;
|
|
2335
2643
|
engine = role === "stt"
|
|
2336
2644
|
? await createWhisperEngine(namespace, configuration, signal, report)
|
|
2337
2645
|
: await createKokoroEngine(namespace, configuration, signal, report);
|
|
2338
2646
|
lifecycleReason = `${role}-load-completed`;
|
|
2339
|
-
workerProgress(send, role, request.id, `${role}-provider-ready`, 1, 1);
|
|
2340
2647
|
return status();
|
|
2341
2648
|
} catch (error) {
|
|
2342
2649
|
const failure = admitWorkerFailure(
|
|
@@ -2476,12 +2783,15 @@ export function createSpeechWorkerRuntime({ role, scope = globalThis, send } = {
|
|
|
2476
2783
|
id: request.id,
|
|
2477
2784
|
ok: true,
|
|
2478
2785
|
result: result ?? null,
|
|
2479
|
-
}, collectSpeechTransferables(result)), (error) =>
|
|
2480
|
-
|
|
2481
|
-
|
|
2482
|
-
|
|
2483
|
-
|
|
2484
|
-
|
|
2786
|
+
}, collectSpeechTransferables(result)), (error) => {
|
|
2787
|
+
const response = {
|
|
2788
|
+
protocol: SPEECH_WORKER_PROTOCOL,
|
|
2789
|
+
id: request.id,
|
|
2790
|
+
ok: false,
|
|
2791
|
+
error: null,
|
|
2792
|
+
};
|
|
2793
|
+
sendSerializedError(send, response, error, role, op, scope);
|
|
2794
|
+
});
|
|
2485
2795
|
}
|
|
2486
2796
|
|
|
2487
2797
|
function handleMessage(request) {
|
|
@@ -2507,7 +2817,7 @@ export function createSpeechWorkerRuntime({ role, scope = globalThis, send } = {
|
|
|
2507
2817
|
if (op === "cancel") {
|
|
2508
2818
|
const target = operations.get(request.payload?.targetId);
|
|
2509
2819
|
target?.controller.abort(operationReason(role, target.op, "cancelled"));
|
|
2510
|
-
const result = Promise.resolve(
|
|
2820
|
+
const result = Promise.resolve(completeValue({
|
|
2511
2821
|
cancelled: Boolean(target),
|
|
2512
2822
|
reason: target
|
|
2513
2823
|
? operationReason(role, target.op, "cancelled")
|
|
@@ -2532,24 +2842,14 @@ export function createSpeechWorkerRuntime({ role, scope = globalThis, send } = {
|
|
|
2532
2842
|
return operation;
|
|
2533
2843
|
}
|
|
2534
2844
|
|
|
2535
|
-
return
|
|
2536
|
-
}
|
|
2537
|
-
|
|
2538
|
-
function privatePort(value) {
|
|
2539
|
-
return value
|
|
2540
|
-
&& typeof value.postMessage === "function"
|
|
2541
|
-
&& typeof value.addEventListener === "function"
|
|
2542
|
-
? value
|
|
2543
|
-
: null;
|
|
2845
|
+
return completeValue({ handleMessage, status });
|
|
2544
2846
|
}
|
|
2545
2847
|
|
|
2546
2848
|
export function installBrowserSpeechWorker(role, scope = globalThis) {
|
|
2547
|
-
let transport = scope;
|
|
2548
|
-
let transportMode = null;
|
|
2549
2849
|
const runtime = createSpeechWorkerRuntime({
|
|
2550
2850
|
role,
|
|
2551
2851
|
scope,
|
|
2552
|
-
send: (message, transfers) =>
|
|
2852
|
+
send: (message, transfers) => scope.postMessage(message, transfers),
|
|
2553
2853
|
});
|
|
2554
2854
|
|
|
2555
2855
|
function receive(request) {
|
|
@@ -2557,36 +2857,6 @@ export function installBrowserSpeechWorker(role, scope = globalThis) {
|
|
|
2557
2857
|
}
|
|
2558
2858
|
|
|
2559
2859
|
scope.addEventListener("message", (event) => {
|
|
2560
|
-
if (transportMode === "private-message-port") return;
|
|
2561
|
-
const requestedPort = privatePort(event.data?.privatePort);
|
|
2562
|
-
const isGraphLoad = event.data?.op === "load"
|
|
2563
|
-
&& graphConfiguration(event.data?.payload?.configuration);
|
|
2564
|
-
if (requestedPort) {
|
|
2565
|
-
if (!isGraphLoad || transportMode !== null) return;
|
|
2566
|
-
transportMode = "private-message-port";
|
|
2567
|
-
transport = requestedPort;
|
|
2568
|
-
requestedPort.addEventListener("message", (portEvent) => receive(portEvent.data));
|
|
2569
|
-
requestedPort.start?.();
|
|
2570
|
-
const { privatePort: ignored, ...request } = event.data;
|
|
2571
|
-
void ignored;
|
|
2572
|
-
receive(request);
|
|
2573
|
-
return;
|
|
2574
|
-
}
|
|
2575
|
-
if (isGraphLoad) {
|
|
2576
|
-
scope.postMessage({
|
|
2577
|
-
protocol: SPEECH_WORKER_PROTOCOL,
|
|
2578
|
-
id: event.data.id,
|
|
2579
|
-
ok: false,
|
|
2580
|
-
error: serializedError(workerError(
|
|
2581
|
-
"ARCANE_AI_ARTIFACT_GRAPH_ISOLATION_UNAVAILABLE",
|
|
2582
|
-
"Authenticated artifact graph loading requires a private MessagePort.",
|
|
2583
|
-
undefined,
|
|
2584
|
-
"artifact-graph-private-message-port-missing",
|
|
2585
|
-
), role, "load"),
|
|
2586
|
-
});
|
|
2587
|
-
return;
|
|
2588
|
-
}
|
|
2589
|
-
transportMode ??= "worker-global-message";
|
|
2590
2860
|
receive(event.data);
|
|
2591
2861
|
});
|
|
2592
2862
|
return runtime;
|
|
@@ -2613,7 +2883,7 @@ export function installBrowserSpeechArtifactModuleWorker(role, scope = globalThi
|
|
|
2613
2883
|
}
|
|
2614
2884
|
const request = event.data;
|
|
2615
2885
|
if (request?.protocol !== NESTED_WORKER_PROTOCOL
|
|
2616
|
-
|| request.op !== "initialize-
|
|
2886
|
+
|| request.op !== "initialize-artifact-module-worker"
|
|
2617
2887
|
|| request.role !== role) return;
|
|
2618
2888
|
initializing = true;
|
|
2619
2889
|
void (async () => {
|
|
@@ -2630,7 +2900,7 @@ export function installBrowserSpeechArtifactModuleWorker(role, scope = globalThi
|
|
|
2630
2900
|
"artifact-graph-module-worker-target-not-materialized",
|
|
2631
2901
|
);
|
|
2632
2902
|
}
|
|
2633
|
-
environment =
|
|
2903
|
+
environment = installOrdinaryArtifactModuleRouter(scope, configuration, role);
|
|
2634
2904
|
await import(target.moduleUrl);
|
|
2635
2905
|
scope.removeEventListener("message", bootstrap);
|
|
2636
2906
|
await new Promise((resolve) => queueMicrotask(resolve));
|
|
@@ -2641,17 +2911,27 @@ export function installBrowserSpeechArtifactModuleWorker(role, scope = globalThi
|
|
|
2641
2911
|
} catch {
|
|
2642
2912
|
// Preserve the exact nested Worker bootstrap failure.
|
|
2643
2913
|
}
|
|
2644
|
-
|
|
2645
|
-
|
|
2646
|
-
|
|
2647
|
-
|
|
2648
|
-
|
|
2649
|
-
|
|
2914
|
+
try {
|
|
2915
|
+
sendSerializedError(
|
|
2916
|
+
(message, transfers) => scope.postMessage(message, transfers),
|
|
2917
|
+
{
|
|
2918
|
+
protocol: NESTED_WORKER_PROTOCOL,
|
|
2919
|
+
event: "artifact-module-worker-bootstrap-rejected",
|
|
2920
|
+
error: null,
|
|
2921
|
+
},
|
|
2922
|
+
error,
|
|
2923
|
+
role,
|
|
2924
|
+
"load",
|
|
2925
|
+
scope,
|
|
2926
|
+
);
|
|
2927
|
+
} finally {
|
|
2928
|
+
scope.close?.();
|
|
2929
|
+
}
|
|
2650
2930
|
}
|
|
2651
2931
|
})();
|
|
2652
2932
|
};
|
|
2653
2933
|
scope.addEventListener("message", bootstrap);
|
|
2654
|
-
return
|
|
2934
|
+
return completeValue({
|
|
2655
2935
|
protocol: NESTED_WORKER_PROTOCOL,
|
|
2656
2936
|
role,
|
|
2657
2937
|
lifecycleStatus: `${role}-artifact-module-worker-awaiting-initialization`,
|