arcane-os 0.3.1 → 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 +14 -0
- package/README.md +86 -117
- package/bin/arcane-test.mjs +170 -46
- package/browser-runtime/ai/browser-speech-artifacts.mjs +855 -895
- package/browser-runtime/ai/browser-speech-providers.mjs +80 -204
- 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 -148
- package/browser-runtime/ai/speech-worker-runtime.mjs +642 -374
- 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 +1042 -427
- package/runtime/arcane/modules/AIProviderRuntime.js +618 -359
- 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 +27 -67
- 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 +109 -1558
- 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 -185
- package/docs/reference/ai/browser-speech-package-authority.json +0 -835
- package/docs/reference/ai/browser-speech.md +0 -1295
- 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 -719
- package/docs/reference/runtime-components.md +0 -1366
- package/docs/reference/runtime-entities.md +0 -303
- package/docs/reference/runtime-modules.md +0 -2965
- package/docs/reference/sdk-api.md +0 -6698
- 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,36 +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
|
-
const security = configuration.security;
|
|
699
|
-
const securityChecks = security?.checks;
|
|
700
|
-
if (
|
|
701
|
-
!security
|
|
702
|
-
|| typeof security !== "object"
|
|
703
|
-
|| Array.isArray(security)
|
|
704
|
-
|| typeof security.secure !== "boolean"
|
|
705
|
-
|| !securityChecks
|
|
706
|
-
|| typeof securityChecks !== "object"
|
|
707
|
-
|| Array.isArray(securityChecks)
|
|
708
|
-
|| typeof securityChecks.byteLength !== "boolean"
|
|
709
|
-
|| typeof securityChecks.sha256 !== "boolean"
|
|
710
|
-
|| typeof configuration.runtime.artifactGraphId !== "string"
|
|
711
|
-
|| !/^[a-f0-9]{64}$/u.test(configuration.runtime.artifactGraphId)
|
|
712
|
-
|| (
|
|
713
|
-
typeof configuration.runtime.guardCapability !== "string"
|
|
714
|
-
|| !/^[a-f0-9]{64}$/u.test(configuration.runtime.guardCapability)
|
|
715
|
-
|| !GRAPH_ADMISSIONS.has(configuration.runtime.artifactGraphAdmission)
|
|
716
|
-
|| !configuration.runtime.edges
|
|
717
|
-
|| typeof configuration.runtime.edges !== "object"
|
|
718
|
-
|| !Array.isArray(configuration.runtime.transforms)
|
|
719
|
-
)
|
|
720
|
-
) {
|
|
721
|
-
throw workerError(
|
|
722
|
-
"ARCANE_AI_ARTIFACT_GRAPH_CONFIGURATION_INVALID",
|
|
723
|
-
"Artifact graph security, identity, admission, edges, and transforms are required.",
|
|
724
|
-
undefined,
|
|
725
|
-
"artifact-graph-worker-configuration-incomplete",
|
|
726
|
-
);
|
|
727
|
-
}
|
|
728
932
|
const wasm = configuration.runtime.onnxWasm;
|
|
729
933
|
if (
|
|
730
934
|
!wasm
|
|
@@ -732,17 +936,17 @@ function validateConfiguration(configuration, role) {
|
|
|
732
936
|
|| !runtimePaths.has(wasm.mjsPath)
|
|
733
937
|
|| !runtimePaths.has(wasm.wasmPath)
|
|
734
938
|
) {
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
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.",
|
|
738
942
|
undefined,
|
|
739
943
|
"artifact-graph-onnx-wasm-configuration-mismatch",
|
|
740
944
|
);
|
|
741
945
|
}
|
|
742
946
|
if (role === "tts" && wasm.numThreads !== undefined) {
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
947
|
+
throw workerError(
|
|
948
|
+
"ARCANE_AI_ARTIFACT_GRAPH_CONFIGURATION_INVALID",
|
|
949
|
+
"Kokoro does not expose a numThreads configuration field.",
|
|
746
950
|
undefined,
|
|
747
951
|
"kokoro-env-num-threads-field-not-exposed",
|
|
748
952
|
);
|
|
@@ -773,17 +977,6 @@ function validateConfiguration(configuration, role) {
|
|
|
773
977
|
);
|
|
774
978
|
}
|
|
775
979
|
}
|
|
776
|
-
if (
|
|
777
|
-
configuration.runtime.negativeRuntimeRequestUrls !== undefined
|
|
778
|
-
&& !Array.isArray(configuration.runtime.negativeRuntimeRequestUrls)
|
|
779
|
-
) {
|
|
780
|
-
throw workerError(
|
|
781
|
-
"ARCANE_AI_ARTIFACT_GRAPH_CONFIGURATION_INVALID",
|
|
782
|
-
"Artifact graph negative runtime request routes must be an array.",
|
|
783
|
-
undefined,
|
|
784
|
-
"artifact-graph-negative-request-routes-not-array",
|
|
785
|
-
);
|
|
786
|
-
}
|
|
787
980
|
}
|
|
788
981
|
return configuration;
|
|
789
982
|
}
|
|
@@ -804,6 +997,9 @@ function absoluteRequestUrl(value, scope) {
|
|
|
804
997
|
}
|
|
805
998
|
}
|
|
806
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.
|
|
807
1003
|
function createArtifactRoutes(scope, configuration) {
|
|
808
1004
|
const positive = new Map();
|
|
809
1005
|
const negative = new Set();
|
|
@@ -843,7 +1039,7 @@ function createArtifactRoutes(scope, configuration) {
|
|
|
843
1039
|
}
|
|
844
1040
|
negative.add(absolute);
|
|
845
1041
|
}
|
|
846
|
-
return
|
|
1042
|
+
return completeValue({ files, positive, negative });
|
|
847
1043
|
}
|
|
848
1044
|
|
|
849
1045
|
function requestMethod(input, init, scope) {
|
|
@@ -961,16 +1157,16 @@ function createRouteReader(scope, configuration, originalFetch) {
|
|
|
961
1157
|
function resolve(input) {
|
|
962
1158
|
const absolute = absoluteRequestUrl(input, scope);
|
|
963
1159
|
const file = routes.positive.get(absolute);
|
|
964
|
-
if (file) return
|
|
1160
|
+
if (file) return completeValue({ kind: "file", absolute, file });
|
|
965
1161
|
if (routes.negative.has(absolute)) {
|
|
966
|
-
return
|
|
1162
|
+
return completeValue({ kind: "negative", absolute, file: null });
|
|
967
1163
|
}
|
|
968
1164
|
return null;
|
|
969
1165
|
}
|
|
970
1166
|
|
|
971
1167
|
function cacheForPaths(targetPaths) {
|
|
972
1168
|
const admittedPaths = new Set(targetPaths);
|
|
973
|
-
return
|
|
1169
|
+
return completeValue({
|
|
974
1170
|
async match(input) {
|
|
975
1171
|
const resolution = resolve(input);
|
|
976
1172
|
if (
|
|
@@ -1015,13 +1211,13 @@ function createRouteReader(scope, configuration, originalFetch) {
|
|
|
1015
1211
|
return false;
|
|
1016
1212
|
},
|
|
1017
1213
|
async keys() {
|
|
1018
|
-
return
|
|
1214
|
+
return completeValue([]);
|
|
1019
1215
|
},
|
|
1020
1216
|
});
|
|
1021
1217
|
}
|
|
1022
1218
|
|
|
1023
1219
|
const cache = cacheForPaths(routes.files.map((file) => file.path));
|
|
1024
|
-
return
|
|
1220
|
+
return completeValue({ cache, cacheForPaths, resolve, responseFor, routes });
|
|
1025
1221
|
}
|
|
1026
1222
|
|
|
1027
1223
|
function installLegacyAuthorizedFetch(scope, configuration) {
|
|
@@ -1061,11 +1257,11 @@ function installLegacyAuthorizedFetch(scope, configuration) {
|
|
|
1061
1257
|
authorized,
|
|
1062
1258
|
"speech-worker-fetch-isolation-unavailable",
|
|
1063
1259
|
);
|
|
1064
|
-
return
|
|
1260
|
+
return completeValue({ cache: null, cleanup: restore });
|
|
1065
1261
|
}
|
|
1066
1262
|
|
|
1067
1263
|
function installDeniedCacheIsolation(scope) {
|
|
1068
|
-
const denied =
|
|
1264
|
+
const denied = completeValue({
|
|
1069
1265
|
async open() {
|
|
1070
1266
|
throw workerError(
|
|
1071
1267
|
"ARCANE_AI_UNDECLARED_ARTIFACT",
|
|
@@ -1086,7 +1282,7 @@ function installDeniedCacheIsolation(scope) {
|
|
|
1086
1282
|
return false;
|
|
1087
1283
|
},
|
|
1088
1284
|
async keys() {
|
|
1089
|
-
return
|
|
1285
|
+
return completeValue([]);
|
|
1090
1286
|
},
|
|
1091
1287
|
async delete() {
|
|
1092
1288
|
return false;
|
|
@@ -1234,7 +1430,7 @@ function typedArrayIntrinsics(scope) {
|
|
|
1234
1430
|
"artifact-graph-typed-array-validation-unavailable",
|
|
1235
1431
|
);
|
|
1236
1432
|
}
|
|
1237
|
-
return
|
|
1433
|
+
return completeValue({
|
|
1238
1434
|
constructors,
|
|
1239
1435
|
dataViewPrototype: DataViewConstructor?.prototype ?? null,
|
|
1240
1436
|
getPrototypeOf: Object.getPrototypeOf,
|
|
@@ -1304,6 +1500,9 @@ function nestedWorkerFailureEvent(scope, error) {
|
|
|
1304
1500
|
return event;
|
|
1305
1501
|
}
|
|
1306
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.
|
|
1307
1506
|
function createArtifactGraphGuard(scope, configuration, role, reader, originalWorker) {
|
|
1308
1507
|
const files = runtimeFileMap(configuration);
|
|
1309
1508
|
const edges = configuration.runtime.edges;
|
|
@@ -1366,7 +1565,7 @@ function createArtifactGraphGuard(scope, configuration, role, reader, originalWo
|
|
|
1366
1565
|
return true;
|
|
1367
1566
|
}
|
|
1368
1567
|
|
|
1369
|
-
const guard =
|
|
1568
|
+
const guard = completeValue({
|
|
1370
1569
|
protocol: "arcane-ai-browser-speech-artifact-graph-runtime/1",
|
|
1371
1570
|
|
|
1372
1571
|
async dynamicImport(capability, modulePath, occurrence, specifier) {
|
|
@@ -1487,7 +1686,7 @@ function createArtifactGraphGuard(scope, configuration, role, reader, originalWo
|
|
|
1487
1686
|
event.stopImmediatePropagation?.();
|
|
1488
1687
|
const admitted = normalizeSpeechWorkerErrorEnvelope(event.data.error, role, "load");
|
|
1489
1688
|
const failure = admitted
|
|
1490
|
-
? workerError(admitted.code, admitted.message,
|
|
1689
|
+
? workerError(admitted.code, admitted.message, admitted.cause, admitted.reason)
|
|
1491
1690
|
: workerError(
|
|
1492
1691
|
"ARCANE_AI_WORKER_MESSAGE_ERROR",
|
|
1493
1692
|
"The authenticated artifact module Worker error envelope was rejected.",
|
|
@@ -1555,7 +1754,7 @@ function createArtifactGraphGuard(scope, configuration, role, reader, originalWo
|
|
|
1555
1754
|
},
|
|
1556
1755
|
});
|
|
1557
1756
|
|
|
1558
|
-
return
|
|
1757
|
+
return completeValue({
|
|
1559
1758
|
guard,
|
|
1560
1759
|
transformersCache: (() => {
|
|
1561
1760
|
const edgesForTransformers = [...cacheOpens.values()].filter((edge) =>
|
|
@@ -1650,7 +1849,6 @@ function installStringTimerIsolation(scope, name) {
|
|
|
1650
1849
|
function installArtifactGraphEnvironment(scope, configuration, role) {
|
|
1651
1850
|
const originalFetch = scope.fetch?.bind(scope);
|
|
1652
1851
|
const originalWorker = scope.Worker;
|
|
1653
|
-
const strictSecurity = configuration.security?.secure === true;
|
|
1654
1852
|
if (typeof originalFetch !== "function") {
|
|
1655
1853
|
throw workerError(
|
|
1656
1854
|
"ARCANE_AI_PROVIDER_UNAVAILABLE",
|
|
@@ -1677,120 +1875,18 @@ function installArtifactGraphEnvironment(scope, configuration, role) {
|
|
|
1677
1875
|
);
|
|
1678
1876
|
const restores = [];
|
|
1679
1877
|
try {
|
|
1680
|
-
if (strictSecurity) {
|
|
1681
|
-
restores.push(installDynamicCodeConstructorIsolation());
|
|
1682
|
-
restores.push(installStringTimerIsolation(scope, "setInterval"));
|
|
1683
|
-
restores.push(installStringTimerIsolation(scope, "setTimeout"));
|
|
1684
|
-
if ("indexedDB" in scope) {
|
|
1685
|
-
restores.push(installScopeValue(
|
|
1686
|
-
scope,
|
|
1687
|
-
"indexedDB",
|
|
1688
|
-
undefined,
|
|
1689
|
-
"artifact-graph-indexeddb-isolation-unavailable",
|
|
1690
|
-
));
|
|
1691
|
-
}
|
|
1692
|
-
if (scope.navigator && "storage" in scope.navigator) {
|
|
1693
|
-
restores.push(installObjectValue(
|
|
1694
|
-
scope.navigator,
|
|
1695
|
-
"storage",
|
|
1696
|
-
undefined,
|
|
1697
|
-
"WorkerNavigator.storage",
|
|
1698
|
-
"artifact-graph-opfs-isolation-unavailable",
|
|
1699
|
-
));
|
|
1700
|
-
}
|
|
1701
|
-
}
|
|
1702
1878
|
restores.push(installScopeValue(
|
|
1703
1879
|
scope,
|
|
1704
1880
|
GRAPH_GUARD_NAME,
|
|
1705
1881
|
graphGuard.guard,
|
|
1706
1882
|
"artifact-graph-guard-global-definition-rejected",
|
|
1707
1883
|
));
|
|
1708
|
-
if (strictSecurity) {
|
|
1709
|
-
restores.push(installScopeValue(
|
|
1710
|
-
scope,
|
|
1711
|
-
"fetch",
|
|
1712
|
-
async function rejectUntransformedArtifactGraphFetch() {
|
|
1713
|
-
throw workerError(
|
|
1714
|
-
"ARCANE_AI_ARTIFACT_GRAPH_FETCH_EDGE_UNDECLARED",
|
|
1715
|
-
"Artifact graph fetch must use its declared transformed edge.",
|
|
1716
|
-
undefined,
|
|
1717
|
-
"artifact-graph-fetch-guard-bypassed",
|
|
1718
|
-
);
|
|
1719
|
-
},
|
|
1720
|
-
"artifact-graph-fetch-isolation-unavailable",
|
|
1721
|
-
));
|
|
1722
|
-
const cacheStorage = Object.freeze({
|
|
1723
|
-
async open() {
|
|
1724
|
-
throw workerError(
|
|
1725
|
-
"ARCANE_AI_ARTIFACT_GRAPH_CACHE_EDGE_UNDECLARED",
|
|
1726
|
-
"Artifact graph CacheStorage.open must use its declared transformed edge.",
|
|
1727
|
-
undefined,
|
|
1728
|
-
"artifact-graph-cache-open-guard-bypassed",
|
|
1729
|
-
);
|
|
1730
|
-
},
|
|
1731
|
-
async match() {
|
|
1732
|
-
throw workerError(
|
|
1733
|
-
"ARCANE_AI_ARTIFACT_GRAPH_CACHE_EDGE_UNDECLARED",
|
|
1734
|
-
"Artifact graph CacheStorage.match requires a declared cache-open edge.",
|
|
1735
|
-
undefined,
|
|
1736
|
-
"artifact-graph-cache-match-guard-bypassed",
|
|
1737
|
-
);
|
|
1738
|
-
},
|
|
1739
|
-
async has() {
|
|
1740
|
-
return true;
|
|
1741
|
-
},
|
|
1742
|
-
async keys() {
|
|
1743
|
-
return Object.freeze([]);
|
|
1744
|
-
},
|
|
1745
|
-
async delete() {
|
|
1746
|
-
return false;
|
|
1747
|
-
},
|
|
1748
|
-
});
|
|
1749
|
-
restores.push(installScopeValue(
|
|
1750
|
-
scope,
|
|
1751
|
-
"caches",
|
|
1752
|
-
cacheStorage,
|
|
1753
|
-
"artifact-graph-cache-isolation-unavailable",
|
|
1754
|
-
));
|
|
1755
|
-
|
|
1756
|
-
const deniedCapabilities = [
|
|
1757
|
-
"BroadcastChannel",
|
|
1758
|
-
"EventSource",
|
|
1759
|
-
"Function",
|
|
1760
|
-
"RTCPeerConnection",
|
|
1761
|
-
"ShadowRealm",
|
|
1762
|
-
"SharedWorker",
|
|
1763
|
-
"WebSocket",
|
|
1764
|
-
"WebSocketStream",
|
|
1765
|
-
"WebTransport",
|
|
1766
|
-
"Worker",
|
|
1767
|
-
"XMLHttpRequest",
|
|
1768
|
-
"eval",
|
|
1769
|
-
"importScripts",
|
|
1770
|
-
];
|
|
1771
|
-
for (const name of deniedCapabilities) {
|
|
1772
|
-
if (!(name in scope)) continue;
|
|
1773
|
-
restores.push(installScopeValue(
|
|
1774
|
-
scope,
|
|
1775
|
-
name,
|
|
1776
|
-
function rejectUndeclaredArtifactGraphCapability() {
|
|
1777
|
-
throw workerError(
|
|
1778
|
-
"ARCANE_AI_ARTIFACT_GRAPH_ISOLATION_UNAVAILABLE",
|
|
1779
|
-
`The authenticated artifact graph denied raw ${name} access.`,
|
|
1780
|
-
undefined,
|
|
1781
|
-
`artifact-graph-${name.toLowerCase()}-capability-undeclared`,
|
|
1782
|
-
);
|
|
1783
|
-
},
|
|
1784
|
-
`artifact-graph-${name.toLowerCase()}-isolation-unavailable`,
|
|
1785
|
-
));
|
|
1786
|
-
}
|
|
1787
|
-
}
|
|
1788
1884
|
} catch (error) {
|
|
1789
1885
|
restoreScopeValues(restores);
|
|
1790
1886
|
graphGuard.cleanup();
|
|
1791
1887
|
throw error;
|
|
1792
1888
|
}
|
|
1793
|
-
return
|
|
1889
|
+
return completeValue({
|
|
1794
1890
|
cache: graphGuard.transformersCache,
|
|
1795
1891
|
cleanup() {
|
|
1796
1892
|
graphGuard.cleanup();
|
|
@@ -1799,6 +1895,300 @@ function installArtifactGraphEnvironment(scope, configuration, role) {
|
|
|
1799
1895
|
});
|
|
1800
1896
|
}
|
|
1801
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
|
+
|
|
1802
2192
|
function assignSetting(object, name, value, cleanup, {
|
|
1803
2193
|
allowCreate = false,
|
|
1804
2194
|
assignmentRejectedReason,
|
|
@@ -1856,14 +2246,6 @@ function assignSetting(object, name, value, cleanup, {
|
|
|
1856
2246
|
|
|
1857
2247
|
function configuredWasmPaths(configuration) {
|
|
1858
2248
|
if (configuration.runtime.wasmPaths !== undefined) {
|
|
1859
|
-
if (configuration.security?.secure === true) {
|
|
1860
|
-
throw workerError(
|
|
1861
|
-
"ARCANE_AI_INVALID_REQUEST",
|
|
1862
|
-
"Secure browser speech cannot use remote wasmPaths.",
|
|
1863
|
-
undefined,
|
|
1864
|
-
"speech-worker-secure-remote-wasm-paths-rejected",
|
|
1865
|
-
);
|
|
1866
|
-
}
|
|
1867
2249
|
return configuration.runtime.wasmPaths;
|
|
1868
2250
|
}
|
|
1869
2251
|
const byPath = new Map(configuration.runtime.files.map((file) => [file.path, file]));
|
|
@@ -1884,12 +2266,11 @@ function configuredWasmPaths(configuration) {
|
|
|
1884
2266
|
"artifact-graph-onnx-wasm-pair-not-materialized",
|
|
1885
2267
|
);
|
|
1886
2268
|
}
|
|
1887
|
-
return
|
|
2269
|
+
return completeValue({ mjs: mjs.moduleUrl, wasm: wasm.moduleUrl });
|
|
1888
2270
|
}
|
|
1889
2271
|
|
|
1890
2272
|
function configureRuntimeNamespace(namespace, configuration, role, cache) {
|
|
1891
2273
|
const cleanup = [];
|
|
1892
|
-
const strictSecurity = configuration.security?.secure === true;
|
|
1893
2274
|
const restoreSettings = () => {
|
|
1894
2275
|
for (const restore of cleanup.splice(0).reverse()) {
|
|
1895
2276
|
try {
|
|
@@ -1949,24 +2330,6 @@ function configureRuntimeNamespace(namespace, configuration, role, cache) {
|
|
|
1949
2330
|
assignmentRejectedReason: "transformers-env-allow-remote-models-assignment-rejected",
|
|
1950
2331
|
unavailableReason: "transformers-env-allow-remote-models-unavailable",
|
|
1951
2332
|
});
|
|
1952
|
-
if (strictSecurity) {
|
|
1953
|
-
assignSetting(env, "useBrowserCache", false, cleanup, {
|
|
1954
|
-
assignmentRejectedReason: "transformers-env-browser-cache-assignment-rejected",
|
|
1955
|
-
unavailableReason: "transformers-env-browser-cache-unavailable",
|
|
1956
|
-
});
|
|
1957
|
-
assignSetting(env, "useFSCache", false, cleanup, {
|
|
1958
|
-
assignmentRejectedReason: "transformers-env-fs-cache-assignment-rejected",
|
|
1959
|
-
unavailableReason: "transformers-env-fs-cache-unavailable",
|
|
1960
|
-
});
|
|
1961
|
-
assignSetting(env, "useCustomCache", cache !== null, cleanup, {
|
|
1962
|
-
assignmentRejectedReason: "transformers-env-custom-cache-toggle-assignment-rejected",
|
|
1963
|
-
unavailableReason: "transformers-env-custom-cache-toggle-unavailable",
|
|
1964
|
-
});
|
|
1965
|
-
assignSetting(env, "customCache", cache, cleanup, {
|
|
1966
|
-
assignmentRejectedReason: "transformers-env-custom-cache-assignment-rejected",
|
|
1967
|
-
unavailableReason: "transformers-env-custom-cache-unavailable",
|
|
1968
|
-
});
|
|
1969
|
-
}
|
|
1970
2333
|
if (paths) {
|
|
1971
2334
|
assignSetting(
|
|
1972
2335
|
wasm,
|
|
@@ -2000,39 +2363,6 @@ function configureRuntimeNamespace(namespace, configuration, role, cache) {
|
|
|
2000
2363
|
return restoreSettings;
|
|
2001
2364
|
}
|
|
2002
2365
|
|
|
2003
|
-
function workerProgress(send, role, requestId, phase, completed = 0, total = null, unit = "items") {
|
|
2004
|
-
send({
|
|
2005
|
-
protocol: SPEECH_WORKER_PROTOCOL,
|
|
2006
|
-
event: "progress",
|
|
2007
|
-
requestId,
|
|
2008
|
-
progress: Object.freeze({
|
|
2009
|
-
phase,
|
|
2010
|
-
completed,
|
|
2011
|
-
total,
|
|
2012
|
-
unit,
|
|
2013
|
-
heartbeat: true,
|
|
2014
|
-
}),
|
|
2015
|
-
}, []);
|
|
2016
|
-
}
|
|
2017
|
-
|
|
2018
|
-
function upstreamProgress(send, role, requestId) {
|
|
2019
|
-
return function reportSpeechModelProgress(update = {}) {
|
|
2020
|
-
const completed = Number.isFinite(update.loaded)
|
|
2021
|
-
? update.loaded
|
|
2022
|
-
: Number.isFinite(update.progress) ? update.progress : 0;
|
|
2023
|
-
const total = Number.isFinite(update.total) ? update.total : null;
|
|
2024
|
-
workerProgress(
|
|
2025
|
-
send,
|
|
2026
|
-
role,
|
|
2027
|
-
requestId,
|
|
2028
|
-
`${role}-model-load-progress`,
|
|
2029
|
-
completed,
|
|
2030
|
-
total,
|
|
2031
|
-
Number.isFinite(update.loaded) ? "bytes" : "items",
|
|
2032
|
-
);
|
|
2033
|
-
};
|
|
2034
|
-
}
|
|
2035
|
-
|
|
2036
2366
|
async function disposeEngine(engine) {
|
|
2037
2367
|
if (!engine) return;
|
|
2038
2368
|
if (typeof engine.dispose === "function") {
|
|
@@ -2077,12 +2407,12 @@ async function createWhisperEngine(namespace, configuration, signal, report) {
|
|
|
2077
2407
|
}
|
|
2078
2408
|
throw error;
|
|
2079
2409
|
}
|
|
2080
|
-
return
|
|
2410
|
+
return completeValue({
|
|
2081
2411
|
async transcribe(input, { signal: requestSignal } = {}) {
|
|
2082
2412
|
throwIfAborted(requestSignal, "stt-transcription-cancelled");
|
|
2083
2413
|
const output = await transcriber(input.audio, { signal: requestSignal });
|
|
2084
2414
|
throwIfAborted(requestSignal, "stt-transcription-cancelled");
|
|
2085
|
-
return
|
|
2415
|
+
return completeValue({ text: String(output?.text ?? "") });
|
|
2086
2416
|
},
|
|
2087
2417
|
dispose: () => disposeEngine(transcriber),
|
|
2088
2418
|
});
|
|
@@ -2117,7 +2447,7 @@ async function createKokoroEngine(namespace, configuration, signal, report) {
|
|
|
2117
2447
|
}
|
|
2118
2448
|
throw error;
|
|
2119
2449
|
}
|
|
2120
|
-
return
|
|
2450
|
+
return completeValue({
|
|
2121
2451
|
async synthesize(input, { signal: requestSignal } = {}) {
|
|
2122
2452
|
throwIfAborted(requestSignal, "tts-synthesis-cancelled");
|
|
2123
2453
|
const output = await synthesizer.generate(input.text, {
|
|
@@ -2129,7 +2459,7 @@ async function createKokoroEngine(namespace, configuration, signal, report) {
|
|
|
2129
2459
|
const audio = output?.audio instanceof Float32Array
|
|
2130
2460
|
? output.audio
|
|
2131
2461
|
: new Float32Array(output?.audio ?? []);
|
|
2132
|
-
return
|
|
2462
|
+
return completeValue({
|
|
2133
2463
|
audio,
|
|
2134
2464
|
sampleRate: output?.sampling_rate,
|
|
2135
2465
|
voice: input.voice,
|
|
@@ -2167,9 +2497,9 @@ function validateInput(role, payload, configuration) {
|
|
|
2167
2497
|
"stt-transcription-sample-rate-mismatch",
|
|
2168
2498
|
);
|
|
2169
2499
|
}
|
|
2170
|
-
return
|
|
2500
|
+
return completeValue({ audio: payload.audio, sampleRate: inputSampleRate });
|
|
2171
2501
|
}
|
|
2172
|
-
const text =
|
|
2502
|
+
const text = requiredContent(payload?.text, "Kokoro text", "tts-synthesis-text-empty");
|
|
2173
2503
|
const voice = requiredText(
|
|
2174
2504
|
payload?.voice ?? configuration.model.defaultVoice,
|
|
2175
2505
|
"Kokoro voice",
|
|
@@ -2185,15 +2515,15 @@ function validateInput(role, payload, configuration) {
|
|
|
2185
2515
|
);
|
|
2186
2516
|
}
|
|
2187
2517
|
const speed = payload?.speed ?? 1;
|
|
2188
|
-
if (!Number.isFinite(speed) || speed <= 0
|
|
2518
|
+
if (!Number.isFinite(speed) || speed <= 0) {
|
|
2189
2519
|
throw workerError(
|
|
2190
2520
|
"ARCANE_AI_INVALID_REQUEST",
|
|
2191
|
-
"Kokoro speed must be greater than 0
|
|
2521
|
+
"Kokoro speed must be greater than 0.",
|
|
2192
2522
|
undefined,
|
|
2193
|
-
"tts-synthesis-speed-
|
|
2523
|
+
"tts-synthesis-speed-not-positive",
|
|
2194
2524
|
);
|
|
2195
2525
|
}
|
|
2196
|
-
return
|
|
2526
|
+
return completeValue({ text, voice, speed });
|
|
2197
2527
|
}
|
|
2198
2528
|
|
|
2199
2529
|
function validateResult(role, result, configuration) {
|
|
@@ -2206,7 +2536,7 @@ function validateResult(role, result, configuration) {
|
|
|
2206
2536
|
"stt-transcription-result-text-not-string",
|
|
2207
2537
|
);
|
|
2208
2538
|
}
|
|
2209
|
-
return
|
|
2539
|
+
return completeValue({ text: result.text });
|
|
2210
2540
|
}
|
|
2211
2541
|
const outputSampleRate = requiredSampleRate(
|
|
2212
2542
|
configuration.model.outputSampleRate,
|
|
@@ -2239,7 +2569,7 @@ function validateResult(role, result, configuration) {
|
|
|
2239
2569
|
);
|
|
2240
2570
|
}
|
|
2241
2571
|
}
|
|
2242
|
-
return
|
|
2572
|
+
return completeValue({
|
|
2243
2573
|
audio: result.audio,
|
|
2244
2574
|
sampleRate: outputSampleRate,
|
|
2245
2575
|
voice: result.voice,
|
|
@@ -2264,16 +2594,7 @@ export function createSpeechWorkerRuntime({ role, scope = globalThis, send } = {
|
|
|
2264
2594
|
|
|
2265
2595
|
function status() {
|
|
2266
2596
|
const state = disposed ? "disposed" : engine ? "ready" : "unloaded";
|
|
2267
|
-
|
|
2268
|
-
? Object.freeze({
|
|
2269
|
-
secure: configuration.security.secure === true,
|
|
2270
|
-
checks: Object.freeze({
|
|
2271
|
-
byteLength: configuration.security.checks?.byteLength === true,
|
|
2272
|
-
sha256: configuration.security.checks?.sha256 === true,
|
|
2273
|
-
}),
|
|
2274
|
-
})
|
|
2275
|
-
: null;
|
|
2276
|
-
return Object.freeze({
|
|
2597
|
+
return completeValue({
|
|
2277
2598
|
state,
|
|
2278
2599
|
lifecycleStatus: `${role}-worker-${state}`,
|
|
2279
2600
|
lifecycleReason,
|
|
@@ -2281,9 +2602,6 @@ export function createSpeechWorkerRuntime({ role, scope = globalThis, send } = {
|
|
|
2281
2602
|
loaded: engine !== null,
|
|
2282
2603
|
busy: operations.size > 0,
|
|
2283
2604
|
activeOperation: operations.values().next().value?.publicOperation ?? null,
|
|
2284
|
-
security,
|
|
2285
|
-
artifactGraphId: configuration?.runtime?.artifactGraphId ?? null,
|
|
2286
|
-
artifactGraphAdmission: configuration?.runtime?.artifactGraphAdmission ?? null,
|
|
2287
2605
|
});
|
|
2288
2606
|
}
|
|
2289
2607
|
|
|
@@ -2304,32 +2622,13 @@ export function createSpeechWorkerRuntime({ role, scope = globalThis, send } = {
|
|
|
2304
2622
|
const entry = configuration.runtime.files.find((file) =>
|
|
2305
2623
|
file.path === configuration.runtime.entry);
|
|
2306
2624
|
if (graphConfiguration(configuration)) {
|
|
2307
|
-
environment =
|
|
2308
|
-
} else if (configuration.security?.secure === true) {
|
|
2309
|
-
const legacyFetch = installLegacyAuthorizedFetch(scope, configuration);
|
|
2310
|
-
try {
|
|
2311
|
-
const restoreCaches = installDeniedCacheIsolation(scope);
|
|
2312
|
-
environment = Object.freeze({
|
|
2313
|
-
cache: null,
|
|
2314
|
-
cleanup() {
|
|
2315
|
-
try {
|
|
2316
|
-
restoreCaches();
|
|
2317
|
-
} finally {
|
|
2318
|
-
legacyFetch.cleanup();
|
|
2319
|
-
}
|
|
2320
|
-
},
|
|
2321
|
-
});
|
|
2322
|
-
} catch (error) {
|
|
2323
|
-
legacyFetch.cleanup();
|
|
2324
|
-
throw error;
|
|
2325
|
-
}
|
|
2625
|
+
environment = installOrdinaryArtifactModuleRouter(scope, configuration, role);
|
|
2326
2626
|
} else {
|
|
2327
|
-
environment =
|
|
2627
|
+
environment = completeValue({
|
|
2328
2628
|
cache: null,
|
|
2329
2629
|
cleanup() {},
|
|
2330
2630
|
});
|
|
2331
2631
|
}
|
|
2332
|
-
workerProgress(send, role, request.id, `${role}-runtime-import-started`);
|
|
2333
2632
|
loadFailureReason = `${role}-worker-runtime-import-rejected`;
|
|
2334
2633
|
const namespace = await import(entry.moduleUrl);
|
|
2335
2634
|
throwIfAborted(signal, `${role}-load-cancelled`);
|
|
@@ -2339,14 +2638,12 @@ export function createSpeechWorkerRuntime({ role, scope = globalThis, send } = {
|
|
|
2339
2638
|
role,
|
|
2340
2639
|
environment.cache,
|
|
2341
2640
|
);
|
|
2342
|
-
workerProgress(send, role, request.id, `${role}-model-load-started`);
|
|
2343
2641
|
loadFailureReason = `${role}-worker-model-load-rejected`;
|
|
2344
|
-
const report =
|
|
2642
|
+
const report = () => undefined;
|
|
2345
2643
|
engine = role === "stt"
|
|
2346
2644
|
? await createWhisperEngine(namespace, configuration, signal, report)
|
|
2347
2645
|
: await createKokoroEngine(namespace, configuration, signal, report);
|
|
2348
2646
|
lifecycleReason = `${role}-load-completed`;
|
|
2349
|
-
workerProgress(send, role, request.id, `${role}-provider-ready`, 1, 1);
|
|
2350
2647
|
return status();
|
|
2351
2648
|
} catch (error) {
|
|
2352
2649
|
const failure = admitWorkerFailure(
|
|
@@ -2486,12 +2783,15 @@ export function createSpeechWorkerRuntime({ role, scope = globalThis, send } = {
|
|
|
2486
2783
|
id: request.id,
|
|
2487
2784
|
ok: true,
|
|
2488
2785
|
result: result ?? null,
|
|
2489
|
-
}, collectSpeechTransferables(result)), (error) =>
|
|
2490
|
-
|
|
2491
|
-
|
|
2492
|
-
|
|
2493
|
-
|
|
2494
|
-
|
|
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
|
+
});
|
|
2495
2795
|
}
|
|
2496
2796
|
|
|
2497
2797
|
function handleMessage(request) {
|
|
@@ -2517,7 +2817,7 @@ export function createSpeechWorkerRuntime({ role, scope = globalThis, send } = {
|
|
|
2517
2817
|
if (op === "cancel") {
|
|
2518
2818
|
const target = operations.get(request.payload?.targetId);
|
|
2519
2819
|
target?.controller.abort(operationReason(role, target.op, "cancelled"));
|
|
2520
|
-
const result = Promise.resolve(
|
|
2820
|
+
const result = Promise.resolve(completeValue({
|
|
2521
2821
|
cancelled: Boolean(target),
|
|
2522
2822
|
reason: target
|
|
2523
2823
|
? operationReason(role, target.op, "cancelled")
|
|
@@ -2542,24 +2842,14 @@ export function createSpeechWorkerRuntime({ role, scope = globalThis, send } = {
|
|
|
2542
2842
|
return operation;
|
|
2543
2843
|
}
|
|
2544
2844
|
|
|
2545
|
-
return
|
|
2546
|
-
}
|
|
2547
|
-
|
|
2548
|
-
function privatePort(value) {
|
|
2549
|
-
return value
|
|
2550
|
-
&& typeof value.postMessage === "function"
|
|
2551
|
-
&& typeof value.addEventListener === "function"
|
|
2552
|
-
? value
|
|
2553
|
-
: null;
|
|
2845
|
+
return completeValue({ handleMessage, status });
|
|
2554
2846
|
}
|
|
2555
2847
|
|
|
2556
2848
|
export function installBrowserSpeechWorker(role, scope = globalThis) {
|
|
2557
|
-
let transport = scope;
|
|
2558
|
-
let transportMode = null;
|
|
2559
2849
|
const runtime = createSpeechWorkerRuntime({
|
|
2560
2850
|
role,
|
|
2561
2851
|
scope,
|
|
2562
|
-
send: (message, transfers) =>
|
|
2852
|
+
send: (message, transfers) => scope.postMessage(message, transfers),
|
|
2563
2853
|
});
|
|
2564
2854
|
|
|
2565
2855
|
function receive(request) {
|
|
@@ -2567,38 +2857,6 @@ export function installBrowserSpeechWorker(role, scope = globalThis) {
|
|
|
2567
2857
|
}
|
|
2568
2858
|
|
|
2569
2859
|
scope.addEventListener("message", (event) => {
|
|
2570
|
-
if (transportMode === "private-message-port") return;
|
|
2571
|
-
const requestedPort = privatePort(event.data?.privatePort);
|
|
2572
|
-
const isGraphLoad = event.data?.op === "load"
|
|
2573
|
-
&& graphConfiguration(event.data?.payload?.configuration);
|
|
2574
|
-
const isStrictGraphLoad = isGraphLoad
|
|
2575
|
-
&& event.data?.payload?.configuration?.security?.secure === true;
|
|
2576
|
-
if (requestedPort) {
|
|
2577
|
-
if (!isStrictGraphLoad || transportMode !== null) return;
|
|
2578
|
-
transportMode = "private-message-port";
|
|
2579
|
-
transport = requestedPort;
|
|
2580
|
-
requestedPort.addEventListener("message", (portEvent) => receive(portEvent.data));
|
|
2581
|
-
requestedPort.start?.();
|
|
2582
|
-
const { privatePort: ignored, ...request } = event.data;
|
|
2583
|
-
void ignored;
|
|
2584
|
-
receive(request);
|
|
2585
|
-
return;
|
|
2586
|
-
}
|
|
2587
|
-
if (isStrictGraphLoad) {
|
|
2588
|
-
scope.postMessage({
|
|
2589
|
-
protocol: SPEECH_WORKER_PROTOCOL,
|
|
2590
|
-
id: event.data.id,
|
|
2591
|
-
ok: false,
|
|
2592
|
-
error: serializedError(workerError(
|
|
2593
|
-
"ARCANE_AI_ARTIFACT_GRAPH_ISOLATION_UNAVAILABLE",
|
|
2594
|
-
"Strict artifact graph loading requires a private MessagePort.",
|
|
2595
|
-
undefined,
|
|
2596
|
-
"artifact-graph-private-message-port-missing",
|
|
2597
|
-
), role, "load"),
|
|
2598
|
-
});
|
|
2599
|
-
return;
|
|
2600
|
-
}
|
|
2601
|
-
transportMode ??= "worker-global-message";
|
|
2602
2860
|
receive(event.data);
|
|
2603
2861
|
});
|
|
2604
2862
|
return runtime;
|
|
@@ -2625,7 +2883,7 @@ export function installBrowserSpeechArtifactModuleWorker(role, scope = globalThi
|
|
|
2625
2883
|
}
|
|
2626
2884
|
const request = event.data;
|
|
2627
2885
|
if (request?.protocol !== NESTED_WORKER_PROTOCOL
|
|
2628
|
-
|| request.op !== "initialize-
|
|
2886
|
+
|| request.op !== "initialize-artifact-module-worker"
|
|
2629
2887
|
|| request.role !== role) return;
|
|
2630
2888
|
initializing = true;
|
|
2631
2889
|
void (async () => {
|
|
@@ -2642,7 +2900,7 @@ export function installBrowserSpeechArtifactModuleWorker(role, scope = globalThi
|
|
|
2642
2900
|
"artifact-graph-module-worker-target-not-materialized",
|
|
2643
2901
|
);
|
|
2644
2902
|
}
|
|
2645
|
-
environment =
|
|
2903
|
+
environment = installOrdinaryArtifactModuleRouter(scope, configuration, role);
|
|
2646
2904
|
await import(target.moduleUrl);
|
|
2647
2905
|
scope.removeEventListener("message", bootstrap);
|
|
2648
2906
|
await new Promise((resolve) => queueMicrotask(resolve));
|
|
@@ -2653,17 +2911,27 @@ export function installBrowserSpeechArtifactModuleWorker(role, scope = globalThi
|
|
|
2653
2911
|
} catch {
|
|
2654
2912
|
// Preserve the exact nested Worker bootstrap failure.
|
|
2655
2913
|
}
|
|
2656
|
-
|
|
2657
|
-
|
|
2658
|
-
|
|
2659
|
-
|
|
2660
|
-
|
|
2661
|
-
|
|
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
|
+
}
|
|
2662
2930
|
}
|
|
2663
2931
|
})();
|
|
2664
2932
|
};
|
|
2665
2933
|
scope.addEventListener("message", bootstrap);
|
|
2666
|
-
return
|
|
2934
|
+
return completeValue({
|
|
2667
2935
|
protocol: NESTED_WORKER_PROTOCOL,
|
|
2668
2936
|
role,
|
|
2669
2937
|
lifecycleStatus: `${role}-artifact-module-worker-awaiting-initialization`,
|