arcane-os 0.5.12 → 0.5.14
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/browser-runtime/ai/browser-wasm-llm-provider.mjs +11 -10
- package/browser-runtime/ai/browser-wllama-runtime.mjs +2 -1
- package/browser-runtime/ai/model-controller.mjs +6 -5
- package/browser-runtime/ai/speech-worker-client.mjs +45 -3
- package/browser-runtime/event-manager.mjs +2 -1
- package/browser-runtime/logging.mjs +58 -0
- package/docs/reference/README.md +8 -7
- package/docs/reference/ai/browser-speech.md +46 -1
- package/docs/reference/asset-versioning.md +69 -0
- package/docs/reference/inventory/package-api.json +30 -2
- package/docs/reference/runtime-modules.md +18 -0
- package/docs/reference/sdk-api.md +94 -8
- package/package.json +3 -2
- package/runtime/arcane/components/app-bar.html +3 -1
- package/runtime/arcane/components/calculator.html +1 -1
- package/runtime/arcane/components/chart.html +2 -7
- package/runtime/arcane/components/chat.html +22 -20
- package/runtime/arcane/components/dashboard-config.html +6 -9
- package/runtime/arcane/components/data-maintenance.html +4 -2
- package/runtime/arcane/components/data-view.html +3 -1
- package/runtime/arcane/components/directory-picker.html +3 -1
- package/runtime/arcane/components/file-manager.html +17 -15
- package/runtime/arcane/components/header.html +5 -3
- package/runtime/arcane/components/markdown-document.html +4 -7
- package/runtime/arcane/components/markdown-editor.html +6 -9
- package/runtime/arcane/components/media-embed.html +1 -1
- package/runtime/arcane/components/modal.html +3 -1
- package/runtime/arcane/components/screen-capture.html +4 -2
- package/runtime/arcane/components/speech.html +10 -8
- package/runtime/arcane/components/table.html +3 -1
- package/runtime/arcane/components/voice-transcription.html +12 -15
- package/runtime/arcane/entities/Chat.js +5 -4
- package/runtime/arcane/entities/User.js +2 -1
- package/runtime/arcane/modules/AI.js +442 -292
- package/runtime/arcane/modules/AIProviderRuntime.js +359 -82
- package/runtime/arcane/modules/CommunicationAppController.js +2 -1
- package/runtime/arcane/modules/ComponentContracts.js +3 -2
- package/runtime/arcane/modules/ConversationTimebox.js +2 -1
- package/runtime/arcane/modules/DBOPFS.js +5 -4
- package/runtime/arcane/modules/Errors.js +3 -14
- package/runtime/arcane/modules/HTMLImport.js +188 -16
- package/runtime/arcane/modules/LocalAIReadinessController.js +2 -1
- package/runtime/arcane/modules/MD.js +3 -2
- package/runtime/arcane/modules/MailOutbox.mjs +2 -1
- package/runtime/arcane/modules/PersistentAIChatSession.js +2 -1
- package/runtime/arcane/modules/ScreenCapture.js +2 -1
- package/runtime/arcane/modules/ThemeBootstrap.js +3 -2
- package/runtime/arcane/modules/ToolCallRouter.js +2 -1
- package/src/dev-server.mjs +72 -5
- package/src/import-map.mjs +603 -16
- package/src/installed-sdk-runtime.mjs +1 -0
- package/src/packager/core.mjs +60 -1
- package/src/workspace-runtime.mjs +19 -7
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { arcaneLogging } from 'arcane-os/logging';
|
|
1
2
|
import ArcaneCommunicationBridge from './ArcaneCommunicationBridge.js';
|
|
2
3
|
import CommunicationHub from './CommunicationHub.js?v=2';
|
|
3
4
|
import CommunicationPreferences from './CommunicationPreferences.js';
|
|
@@ -360,7 +361,7 @@ export default class CommunicationAppController{
|
|
|
360
361
|
hub.dispose();
|
|
361
362
|
}
|
|
362
363
|
}catch(error){
|
|
363
|
-
|
|
364
|
+
arcaneLogging.error('The communication hub could not be disposed cleanly.',error);
|
|
364
365
|
}
|
|
365
366
|
}
|
|
366
367
|
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { arcaneLogging } from 'arcane-os/logging';
|
|
1
2
|
import {
|
|
2
3
|
createArcaneEventSource,
|
|
3
4
|
projectArcaneDOMEvent
|
|
@@ -713,7 +714,7 @@ function createSTTActivationController({
|
|
|
713
714
|
try{
|
|
714
715
|
onChange();
|
|
715
716
|
}catch(error){
|
|
716
|
-
|
|
717
|
+
arcaneLogging.error('Unable to render STT activation state:',error);
|
|
717
718
|
}
|
|
718
719
|
}
|
|
719
720
|
}
|
|
@@ -724,7 +725,7 @@ function createSTTActivationController({
|
|
|
724
725
|
if(nextAction){
|
|
725
726
|
void request(nextAction).catch(
|
|
726
727
|
function reportSTTActivationRequestFailure(error){
|
|
727
|
-
|
|
728
|
+
arcaneLogging.error('The STT activation request could not settle.',error);
|
|
728
729
|
}
|
|
729
730
|
);
|
|
730
731
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { arcaneLogging } from 'arcane-os/logging';
|
|
1
2
|
import {createArcaneEventSource} from 'arcane-os/event-manager';
|
|
2
3
|
|
|
3
4
|
const CONVERSATION_TIMEBOX_TOOL_NAME='conversation_timebox';
|
|
@@ -168,7 +169,7 @@ function conversationTimeboxSubmissionKey(context={}){
|
|
|
168
169
|
}
|
|
169
170
|
|
|
170
171
|
function reportConversationTimeboxListenerError(error){
|
|
171
|
-
|
|
172
|
+
arcaneLogging.error('A conversation timebox listener failed.',error);
|
|
172
173
|
}
|
|
173
174
|
|
|
174
175
|
function conversationTimeboxError(message,code,reason,ErrorType=Error){
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { arcaneLogging } from 'arcane-os/logging';
|
|
1
2
|
import Is from "../../node_modules/strong-type/index.js";
|
|
2
3
|
import {openApplicationDataDirectory} from './AppDataScope.js';
|
|
3
4
|
import {
|
|
@@ -458,7 +459,7 @@ class DBOPFS {
|
|
|
458
459
|
);
|
|
459
460
|
}
|
|
460
461
|
}catch(error){
|
|
461
|
-
|
|
462
|
+
arcaneLogging.error(`Error writing file '${fileName}' to table '${tableName}':`,error)
|
|
462
463
|
throw error
|
|
463
464
|
}
|
|
464
465
|
|
|
@@ -603,7 +604,7 @@ class DBOPFS {
|
|
|
603
604
|
const fileName=entries[index][0]
|
|
604
605
|
|
|
605
606
|
if(result.status!=='fulfilled'){
|
|
606
|
-
|
|
607
|
+
arcaneLogging.error(`Failed to set file '${fileName}':`,result.reason)
|
|
607
608
|
}
|
|
608
609
|
}
|
|
609
610
|
);
|
|
@@ -742,7 +743,7 @@ class DBOPFS {
|
|
|
742
743
|
await table.removeEntry(fileName)
|
|
743
744
|
}catch(error){
|
|
744
745
|
if(error.name!=='NotFoundError'){
|
|
745
|
-
|
|
746
|
+
arcaneLogging.error(error)
|
|
746
747
|
throw error
|
|
747
748
|
}
|
|
748
749
|
}
|
|
@@ -784,7 +785,7 @@ class DBOPFS {
|
|
|
784
785
|
delete this.#tables[tableName]
|
|
785
786
|
delete this.#tableHandles[tableName]
|
|
786
787
|
}catch(error){
|
|
787
|
-
|
|
788
|
+
arcaneLogging.error(error)
|
|
788
789
|
}
|
|
789
790
|
|
|
790
791
|
return true
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import waitForComponent from './WaitForComponent.js';
|
|
2
|
+
import { arcaneLogging,readArcaneDeveloperMode } from 'arcane-os/logging';
|
|
2
3
|
import {
|
|
3
4
|
arcaneEvents,
|
|
4
5
|
createArcaneEventSource
|
|
@@ -154,18 +155,6 @@ async function sendWithWindowMail(target,...args){
|
|
|
154
155
|
return target.mail.send(...args);
|
|
155
156
|
}
|
|
156
157
|
|
|
157
|
-
function defaultDeveloperModeStatus(target){
|
|
158
|
-
try{
|
|
159
|
-
if(target?.user?.ready!==true){
|
|
160
|
-
return null;
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
return target.user.developer===true;
|
|
164
|
-
}catch{
|
|
165
|
-
return false;
|
|
166
|
-
}
|
|
167
|
-
}
|
|
168
|
-
|
|
169
158
|
function appendDeveloperDetail(document,list,label,value){
|
|
170
159
|
if(value===null||value===undefined||value===''){
|
|
171
160
|
return;
|
|
@@ -340,7 +329,7 @@ class Errors {
|
|
|
340
329
|
this.now=options.now||Date.now;
|
|
341
330
|
this.schedule=options.schedule||globalThis.setTimeout.bind(globalThis);
|
|
342
331
|
this.cancel=options.cancel||globalThis.clearTimeout.bind(globalThis);
|
|
343
|
-
this.logger=options.logger||
|
|
332
|
+
this.logger=options.logger||arcaneLogging;
|
|
344
333
|
this.storage=options.storage===undefined
|
|
345
334
|
? defaultStorage(target)
|
|
346
335
|
: options.storage;
|
|
@@ -352,7 +341,7 @@ class Errors {
|
|
|
352
341
|
|| sendWithWindowMail.bind(null,target);
|
|
353
342
|
this.isDeveloperMode=typeof options.isDeveloperMode==='function'
|
|
354
343
|
? options.isDeveloperMode
|
|
355
|
-
:
|
|
344
|
+
: readArcaneDeveloperMode.bind(null,target);
|
|
356
345
|
this.presentDeveloperIncident=typeof options.presentDeveloperIncident==='function'
|
|
357
346
|
? options.presentDeveloperIncident
|
|
358
347
|
: presentDeveloperIncidentModal.bind(null,target);
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { arcaneLogging } from 'arcane-os/logging';
|
|
1
2
|
import {
|
|
2
3
|
createArcaneEventSource,
|
|
3
4
|
projectArcaneDOMEvent
|
|
@@ -12,6 +13,44 @@ globalThis[htmlImportHostRegistryKey]=htmlImportHostRegistry;
|
|
|
12
13
|
|
|
13
14
|
let htmlImportScriptId=0;
|
|
14
15
|
|
|
16
|
+
const htmlImportModuleURL=new URL(import.meta.url);
|
|
17
|
+
const htmlImportAssetVersion=htmlImportModuleURL.searchParams.get('arcaneVersion');
|
|
18
|
+
|
|
19
|
+
function versionComponentResource(value,baseHref){
|
|
20
|
+
if(!htmlImportAssetVersion||typeof value!=='string'||!value||value.startsWith('#')){
|
|
21
|
+
return value;
|
|
22
|
+
}
|
|
23
|
+
let resolvedURL;
|
|
24
|
+
try{
|
|
25
|
+
resolvedURL=new URL(value,baseHref);
|
|
26
|
+
}catch{
|
|
27
|
+
return value;
|
|
28
|
+
}
|
|
29
|
+
if(resolvedURL.protocol!==htmlImportModuleURL.protocol
|
|
30
|
+
||resolvedURL.origin!==htmlImportModuleURL.origin){
|
|
31
|
+
return value;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const fragmentIndex=value.indexOf('#');
|
|
35
|
+
const fragment=fragmentIndex<0?'':value.slice(fragmentIndex);
|
|
36
|
+
const resource=fragmentIndex<0?value:value.slice(0,fragmentIndex);
|
|
37
|
+
const queryIndex=resource.indexOf('?');
|
|
38
|
+
const pathname=queryIndex<0?resource:resource.slice(0,queryIndex);
|
|
39
|
+
const query=queryIndex<0?'':resource.slice(queryIndex+1);
|
|
40
|
+
const versionField=`arcaneVersion=${encodeURIComponent(htmlImportAssetVersion)}`;
|
|
41
|
+
let replaced=false;
|
|
42
|
+
const fields=query?query.split('&').map(function versionQueryField(field){
|
|
43
|
+
if(!new URLSearchParams(field).has('arcaneVersion'))return field;
|
|
44
|
+
if(replaced)return null;
|
|
45
|
+
replaced=true;
|
|
46
|
+
return versionField;
|
|
47
|
+
}).filter(function retainQueryField(field){
|
|
48
|
+
return field!==null;
|
|
49
|
+
}):[];
|
|
50
|
+
if(!replaced)fields.push(versionField);
|
|
51
|
+
return `${pathname}?${fields.join('&')}${fragment}`;
|
|
52
|
+
}
|
|
53
|
+
|
|
15
54
|
function componentRuntimeRoot(resolvedHref){
|
|
16
55
|
const componentURL=new URL(resolvedHref);
|
|
17
56
|
const componentMarker='/arcane/components/';
|
|
@@ -32,28 +71,157 @@ function resolveComponentResource(value,runtimeRoot){
|
|
|
32
71
|
}
|
|
33
72
|
|
|
34
73
|
function resolveComponentStyleResources(styleText,runtimeRoot){
|
|
35
|
-
if(!runtimeRoot)return styleText;
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
74
|
+
if(!runtimeRoot&&!htmlImportAssetVersion)return styleText;
|
|
75
|
+
const parts=[];
|
|
76
|
+
let copiedThrough=0;
|
|
77
|
+
let index=0;
|
|
78
|
+
|
|
79
|
+
function afterComment(start){
|
|
80
|
+
const end=styleText.indexOf('*/',start+2);
|
|
81
|
+
return end<0?styleText.length:end+2;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function afterEscape(start){
|
|
85
|
+
let end=start+1;
|
|
86
|
+
if(/[\da-f]/iu.test(styleText[end]||'')){
|
|
87
|
+
const limit=end+6;
|
|
88
|
+
while(end<limit&&/[\da-f]/iu.test(styleText[end]||''))end+=1;
|
|
89
|
+
if(/[\t\n\f\r ]/u.test(styleText[end]||'')){
|
|
90
|
+
end+=styleText[end]==='\r'&&styleText[end+1]==='\n'?2:1;
|
|
91
|
+
}
|
|
92
|
+
return end;
|
|
41
93
|
}
|
|
42
|
-
|
|
94
|
+
return end+(styleText[end]==='\r'&&styleText[end+1]==='\n'?2:1);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
function afterString(start){
|
|
98
|
+
const quote=styleText[start];
|
|
99
|
+
let end=start+1;
|
|
100
|
+
while(end<styleText.length){
|
|
101
|
+
if(styleText[end]==='\\'){
|
|
102
|
+
end=afterEscape(end);
|
|
103
|
+
}else if(styleText[end]===quote){
|
|
104
|
+
return end+1;
|
|
105
|
+
}else if(/[\n\r\f]/u.test(styleText[end])){
|
|
106
|
+
return end;
|
|
107
|
+
}else{
|
|
108
|
+
end+=1;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
return styleText.length;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
function afterWhitespaceAndComments(start){
|
|
115
|
+
let end=start;
|
|
116
|
+
while(end<styleText.length){
|
|
117
|
+
if(/[\t\n\f\r ]/u.test(styleText[end])){
|
|
118
|
+
end+=1;
|
|
119
|
+
}else if(styleText.startsWith('/*',end)){
|
|
120
|
+
end=afterComment(end);
|
|
121
|
+
}else{
|
|
122
|
+
break;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
return end;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
function appendResource(start,end){
|
|
129
|
+
const resourcePath=styleText.slice(start,end);
|
|
130
|
+
// CSS escapes retain their original spelling until their URL syntax is decoded.
|
|
131
|
+
if(resourcePath.includes('\\'))return;
|
|
132
|
+
const resolved=versionComponentResource(
|
|
133
|
+
resolveComponentResource(resourcePath,runtimeRoot),
|
|
134
|
+
document.baseURI
|
|
135
|
+
);
|
|
136
|
+
if(resolved===resourcePath)return;
|
|
137
|
+
parts.push(styleText.slice(copiedThrough,start),resolved);
|
|
138
|
+
copiedThrough=end;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
while(index<styleText.length){
|
|
142
|
+
if(styleText.startsWith('/*',index)){
|
|
143
|
+
index=afterComment(index);
|
|
144
|
+
continue;
|
|
145
|
+
}
|
|
146
|
+
if(styleText[index]==='"'||styleText[index]==="'"){
|
|
147
|
+
index=afterString(index);
|
|
148
|
+
continue;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
const atRule=styleText[index]==='@';
|
|
152
|
+
const hashToken=styleText[index]==='#';
|
|
153
|
+
const nameStart=atRule||hashToken?index+1:index;
|
|
154
|
+
let nameEnd=nameStart;
|
|
155
|
+
while(nameEnd<styleText.length&&/[-\w\u0080-\uFFFF\\]/u.test(styleText[nameEnd])){
|
|
156
|
+
nameEnd=styleText[nameEnd]==='\\'?afterEscape(nameEnd):nameEnd+1;
|
|
157
|
+
}
|
|
158
|
+
if(nameEnd===nameStart){
|
|
159
|
+
index+=1;
|
|
160
|
+
continue;
|
|
161
|
+
}
|
|
162
|
+
const name=styleText.slice(nameStart,nameEnd).toLowerCase();
|
|
163
|
+
index=nameEnd;
|
|
164
|
+
if(hashToken)continue;
|
|
165
|
+
if(atRule&&name==='import'){
|
|
166
|
+
const start=afterWhitespaceAndComments(nameEnd);
|
|
167
|
+
if(styleText[start]==='"'||styleText[start]==="'"){
|
|
168
|
+
const end=afterString(start);
|
|
169
|
+
if(styleText[end-1]===styleText[start])appendResource(start+1,end-1);
|
|
170
|
+
index=end;
|
|
171
|
+
}
|
|
172
|
+
continue;
|
|
173
|
+
}
|
|
174
|
+
if(atRule||name!=='url'||styleText[nameEnd]!=='(')continue;
|
|
175
|
+
|
|
176
|
+
let start=nameEnd+1;
|
|
177
|
+
while(start<styleText.length&&/[\t\n\f\r ]/u.test(styleText[start]))start+=1;
|
|
178
|
+
if(styleText[start]==='"'||styleText[start]==="'"){
|
|
179
|
+
const end=afterString(start);
|
|
180
|
+
const close=afterWhitespaceAndComments(end);
|
|
181
|
+
if(styleText[end-1]===styleText[start]&&styleText[close]===')'){
|
|
182
|
+
appendResource(start+1,end-1);
|
|
183
|
+
index=close+1;
|
|
184
|
+
}else{
|
|
185
|
+
index=end;
|
|
186
|
+
}
|
|
187
|
+
continue;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
let end=start;
|
|
191
|
+
while(end<styleText.length&&!/[\t\n\f\r )'"(]/u.test(styleText[end])){
|
|
192
|
+
end=styleText[end]==='\\'?afterEscape(end):end+1;
|
|
193
|
+
}
|
|
194
|
+
let close=end;
|
|
195
|
+
while(close<styleText.length&&/[\t\n\f\r ]/u.test(styleText[close]))close+=1;
|
|
196
|
+
if(styleText[close]===')'){
|
|
197
|
+
appendResource(start,end);
|
|
198
|
+
index=close+1;
|
|
199
|
+
}else{
|
|
200
|
+
index=end;
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
if(parts.length===0)return styleText;
|
|
204
|
+
parts.push(styleText.slice(copiedThrough));
|
|
205
|
+
return parts.join('');
|
|
43
206
|
}
|
|
44
207
|
|
|
45
208
|
function createComponentFragment(html,resolvedHref){
|
|
46
209
|
const template=document.createElement('template');
|
|
47
210
|
template.innerHTML=html;
|
|
48
211
|
const runtimeRoot=componentRuntimeRoot(resolvedHref);
|
|
49
|
-
if(!runtimeRoot)return template.content;
|
|
50
212
|
|
|
51
213
|
for(const element of template.content.querySelectorAll('[href],[src]')){
|
|
52
214
|
for(const attribute of ['href','src']){
|
|
53
215
|
if(!element.hasAttribute(attribute))continue;
|
|
54
216
|
const value=element.getAttribute(attribute);
|
|
55
217
|
const resolved=resolveComponentResource(value,runtimeRoot);
|
|
56
|
-
|
|
218
|
+
const navigation=attribute==='href'
|
|
219
|
+
&&['a','area','base'].includes(element.localName);
|
|
220
|
+
const versioned=navigation?resolved:versionComponentResource(
|
|
221
|
+
resolved,
|
|
222
|
+
element.localName==='script'?resolvedHref:document.baseURI
|
|
223
|
+
);
|
|
224
|
+
if(versioned!==value)element.setAttribute(attribute,versioned);
|
|
57
225
|
}
|
|
58
226
|
}
|
|
59
227
|
for(const style of template.content.querySelectorAll('style')){
|
|
@@ -69,7 +237,8 @@ function resolveRelativeDynamicImports(source,baseHref){
|
|
|
69
237
|
return source.replace(
|
|
70
238
|
/\bimport\s*\(\s*(['"])(\.{1,2}\/[^'"]+)\1\s*\)/gu,
|
|
71
239
|
function resolveDynamicImport(_match,_quote,specifier){
|
|
72
|
-
|
|
240
|
+
const resolved=versionComponentResource(new URL(specifier,baseHref).href,baseHref);
|
|
241
|
+
return `import(${JSON.stringify(resolved)})`;
|
|
73
242
|
}
|
|
74
243
|
);
|
|
75
244
|
}
|
|
@@ -131,15 +300,15 @@ class HTMLImport extends HTMLElement {
|
|
|
131
300
|
let resolvedHref='';
|
|
132
301
|
const operationId=`html-import-load-${this.#events.instanceId}-${generation}`;
|
|
133
302
|
if(!href){
|
|
134
|
-
|
|
303
|
+
arcaneLogging.log('no href provided for html-import tag',this);
|
|
135
304
|
this.#loadController=null;
|
|
136
305
|
return;
|
|
137
306
|
}
|
|
138
307
|
|
|
139
308
|
try{
|
|
140
309
|
const resolvedURL=new URL(href,document.baseURI);
|
|
141
|
-
resolvedHref=resolvedURL.href;
|
|
142
|
-
const response=await fetch(
|
|
310
|
+
resolvedHref=versionComponentResource(resolvedURL.href,document.baseURI);
|
|
311
|
+
const response=await fetch(resolvedHref,{
|
|
143
312
|
cache:'default',
|
|
144
313
|
method:'GET',
|
|
145
314
|
signal:controller.signal
|
|
@@ -151,7 +320,7 @@ class HTMLImport extends HTMLElement {
|
|
|
151
320
|
await this.#destroyImportedHost();
|
|
152
321
|
return;
|
|
153
322
|
}
|
|
154
|
-
|
|
323
|
+
arcaneLogging.error('Error loading HTML component:',err);
|
|
155
324
|
const detail={
|
|
156
325
|
code:'HTML_IMPORT_FAILED',
|
|
157
326
|
href,
|
|
@@ -201,7 +370,7 @@ class HTMLImport extends HTMLElement {
|
|
|
201
370
|
}
|
|
202
371
|
|
|
203
372
|
#reportImportedDestroyError(error,{events,href,operationId}){
|
|
204
|
-
|
|
373
|
+
arcaneLogging.error('Error destroying imported HTML component:',error);
|
|
205
374
|
if(!events||events.disposed)return;
|
|
206
375
|
const detail={
|
|
207
376
|
code:'HTML_IMPORT_IMPORTED_COMPONENT_DESTROY_REJECTED',
|
|
@@ -308,7 +477,10 @@ class HTMLImport extends HTMLElement {
|
|
|
308
477
|
let sourceBaseHref=contentBaseHref;
|
|
309
478
|
const scriptSource=script.getAttribute('src');
|
|
310
479
|
if(scriptSource!==null){
|
|
311
|
-
const requestedScriptHref=
|
|
480
|
+
const requestedScriptHref=versionComponentResource(
|
|
481
|
+
new URL(scriptSource,contentBaseHref).href,
|
|
482
|
+
contentBaseHref
|
|
483
|
+
);
|
|
312
484
|
const response=await fetch(requestedScriptHref,{
|
|
313
485
|
cache:'default',
|
|
314
486
|
method:'GET',
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { arcaneLogging } from 'arcane-os/logging';
|
|
1
2
|
import {
|
|
2
3
|
checkLocalAIReadiness,
|
|
3
4
|
deriveLocalAIRequirements
|
|
@@ -287,7 +288,7 @@ export function createLocalAIReadinessController({
|
|
|
287
288
|
function retry(){
|
|
288
289
|
void check().catch(function ignoreDestroyedLocalAIRetry(error){
|
|
289
290
|
if(error?.name!=='AbortError'){
|
|
290
|
-
|
|
291
|
+
arcaneLogging.error('Local AI readiness retry failed.',error);
|
|
291
292
|
}
|
|
292
293
|
});
|
|
293
294
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { arcaneLogging } from 'arcane-os/logging';
|
|
1
2
|
import { marked } from './Marked.min.js';
|
|
2
3
|
import Is from '../../node_modules/strong-type/index.js';
|
|
3
4
|
|
|
@@ -45,7 +46,7 @@ class MD {
|
|
|
45
46
|
|
|
46
47
|
set raw(value=''){
|
|
47
48
|
if(!is.string(value)){
|
|
48
|
-
|
|
49
|
+
arcaneLogging.trace('MD.raw must be a string.');
|
|
49
50
|
return this.#raw;
|
|
50
51
|
}
|
|
51
52
|
this.#raw = value;
|
|
@@ -55,7 +56,7 @@ class MD {
|
|
|
55
56
|
|
|
56
57
|
append(value=''){
|
|
57
58
|
if(!is.string(value)){
|
|
58
|
-
|
|
59
|
+
arcaneLogging.trace('MD.append must be a string.');
|
|
59
60
|
return this.#raw;
|
|
60
61
|
}
|
|
61
62
|
this.#raw += value;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { arcaneLogging } from 'arcane-os/logging';
|
|
1
2
|
export const MAIL_OUTBOX_PROTOCOL='arcane-mail-outbox/1';
|
|
2
3
|
export const MAIL_OUTBOX_TABLE='mail_outbox';
|
|
3
4
|
export const MAIL_OUTBOX_IDEMPOTENCY_WINDOW_MS=24*60*60*1000;
|
|
@@ -34,7 +35,7 @@ function reportMailOutboxObserverError(error){
|
|
|
34
35
|
return;
|
|
35
36
|
}
|
|
36
37
|
}catch{}
|
|
37
|
-
try{
|
|
38
|
+
try{arcaneLogging.error?.(error);}catch{}
|
|
38
39
|
}
|
|
39
40
|
|
|
40
41
|
function fail(message,code='MAIL_OUTBOX_INVALID',ErrorType=Error,cause){
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { arcaneLogging } from 'arcane-os/logging';
|
|
1
2
|
import ChatEntity from '../entities/Chat.js';
|
|
2
3
|
import ConfiguredAIChatSession,{
|
|
3
4
|
normalizeStructuralToolCall
|
|
@@ -475,7 +476,7 @@ class PersistentAIChatSession{
|
|
|
475
476
|
error instanceof Error?error:new Error('The saved chat history is invalid.'),
|
|
476
477
|
'AI_CHAT_INCOHERENT_PERSISTENCE',
|
|
477
478
|
);
|
|
478
|
-
|
|
479
|
+
arcaneLogging.error('Arcane saved chat history is readable but not actionable.',error);
|
|
479
480
|
}
|
|
480
481
|
const storedSystem=storedMessages.find(message=>message.role==='system');
|
|
481
482
|
const initialMessages=storedMessages
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { arcaneLogging } from 'arcane-os/logging';
|
|
1
2
|
import {createArcaneEventSource} from 'arcane-os/event-manager';
|
|
2
3
|
import GifEncoder from './GifEncoder.js';
|
|
3
4
|
|
|
@@ -252,7 +253,7 @@ function supportedRecorderType(Recorder){
|
|
|
252
253
|
|
|
253
254
|
function reportDetachedError(error){
|
|
254
255
|
if(typeof globalThis.reportError==='function')globalThis.reportError(error);
|
|
255
|
-
else
|
|
256
|
+
else arcaneLogging.error?.(error);
|
|
256
257
|
}
|
|
257
258
|
|
|
258
259
|
function monotonicNow(){
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { arcaneLogging } from 'arcane-os/logging';
|
|
1
2
|
import {loadAndApplyTheme} from './ThemeManager.js';
|
|
2
3
|
import {createArcaneEventSource} from 'arcane-os/event-manager';
|
|
3
4
|
|
|
@@ -43,7 +44,7 @@ function installAppearanceListener(ready){
|
|
|
43
44
|
Promise.resolve(ready).then(function reloadTheme(result){
|
|
44
45
|
return result?.manager?.load?.();
|
|
45
46
|
}).catch(function reportThemeReloadFailure(error){
|
|
46
|
-
|
|
47
|
+
arcaneLogging.warn(
|
|
47
48
|
'[Arcane theme] Unable to apply the changed host appearance.',
|
|
48
49
|
error
|
|
49
50
|
);
|
|
@@ -80,7 +81,7 @@ export function bootstrapArcaneTheme(options={}){
|
|
|
80
81
|
}
|
|
81
82
|
|
|
82
83
|
const ready=loadAndApplyTheme(options).catch(error=>{
|
|
83
|
-
|
|
84
|
+
arcaneLogging.warn('[Arcane theme] Unable to load the saved appearance; using the system theme.',error);
|
|
84
85
|
return {manager:null,state:null,error};
|
|
85
86
|
});
|
|
86
87
|
|
|
@@ -1,10 +1,11 @@
|
|
|
1
|
+
import { arcaneLogging } from 'arcane-os/logging';
|
|
1
2
|
function parseArguments(value,name=''){
|
|
2
3
|
let parsed=value;
|
|
3
4
|
if(typeof parsed==='string'){
|
|
4
5
|
try{
|
|
5
6
|
parsed=JSON.parse(parsed);
|
|
6
7
|
}catch(error){
|
|
7
|
-
|
|
8
|
+
arcaneLogging.warn(error);
|
|
8
9
|
throw new Error(`Tool ${name} returned invalid JSON arguments.`);
|
|
9
10
|
}
|
|
10
11
|
}
|
package/src/dev-server.mjs
CHANGED
|
@@ -1,14 +1,19 @@
|
|
|
1
1
|
import {constants as FS_CONSTANTS} from 'node:fs';
|
|
2
|
-
import {lstat,open,realpath} from 'node:fs/promises';
|
|
2
|
+
import {lstat,open,readFile,realpath} from 'node:fs/promises';
|
|
3
3
|
import http from 'node:http';
|
|
4
4
|
import path from 'node:path';
|
|
5
5
|
import {resolveWorkspace} from './workspace.mjs';
|
|
6
6
|
import {createEventQueue} from './event-queue.mjs';
|
|
7
|
+
import {
|
|
8
|
+
inspectImportMapHtml,MANAGED_IMPORT_MAP_ATTRIBUTE,
|
|
9
|
+
readWorkspaceAssetVersion,rewriteAssetReferences
|
|
10
|
+
} from './import-map.mjs';
|
|
7
11
|
|
|
8
12
|
const MIME_TYPES=new Map([
|
|
9
13
|
['.css','text/css; charset=utf-8'],
|
|
10
14
|
['.gif','image/gif'],
|
|
11
15
|
['.html','text/html; charset=utf-8'],
|
|
16
|
+
['.htm','text/html; charset=utf-8'],
|
|
12
17
|
['.ico','image/x-icon'],
|
|
13
18
|
['.jpeg','image/jpeg'],
|
|
14
19
|
['.jpg','image/jpeg'],
|
|
@@ -308,11 +313,21 @@ async function openSafeFile(root,segments){
|
|
|
308
313
|
}
|
|
309
314
|
}
|
|
310
315
|
|
|
311
|
-
async function sendFile(response,opened,{head=false}={}){
|
|
316
|
+
async function sendFile(response,opened,{head=false,assetVersion,revalidate=false,onReference}={}){
|
|
312
317
|
const extension=path.extname(opened.candidate).toLowerCase();
|
|
318
|
+
const content=assetVersion&&/\.(?:m?js|html?|css)$/iu.test(extension)
|
|
319
|
+
?Buffer.from(rewriteAssetReferences(opened.content.toString('utf8'),{
|
|
320
|
+
filePath:opened.candidate,
|
|
321
|
+
version:assetVersion,
|
|
322
|
+
onReference
|
|
323
|
+
}),'utf8')
|
|
324
|
+
:opened.content;
|
|
325
|
+
// Entry documents must discover the current generated graph on navigation.
|
|
326
|
+
// no-cache permits storage and revalidation; ordinary asset caching is unchanged.
|
|
313
327
|
response.writeHead(200,{
|
|
314
328
|
'content-type':MIME_TYPES.get(extension)||'application/octet-stream',
|
|
315
|
-
'content-length':
|
|
329
|
+
'content-length':content.byteLength,
|
|
330
|
+
...(revalidate?{'cache-control':'no-cache'}:{})
|
|
316
331
|
});
|
|
317
332
|
await new Promise((resolve,reject)=>{
|
|
318
333
|
let settled=false;
|
|
@@ -336,7 +351,7 @@ async function sendFile(response,opened,{head=false}={}){
|
|
|
336
351
|
response.once('error',failed);
|
|
337
352
|
response.once('finish',completed);
|
|
338
353
|
response.once('close',completed);
|
|
339
|
-
response.end(head?undefined:
|
|
354
|
+
response.end(head?undefined:content);
|
|
340
355
|
});
|
|
341
356
|
}
|
|
342
357
|
|
|
@@ -509,11 +524,23 @@ async function startOwnedDevServer({
|
|
|
509
524
|
})
|
|
510
525
|
:await packagedRoutes(releaseRoot);
|
|
511
526
|
const mappings=deterministicMappings(routeSet.mappings);
|
|
527
|
+
async function selectedAssetVersion(){
|
|
528
|
+
if(mode!=='source')return undefined;
|
|
529
|
+
return sdkRuntimeSourceRoot===undefined
|
|
530
|
+
?readWorkspaceAssetVersion(routeSet.workspaceRoot)
|
|
531
|
+
:JSON.parse(await readFile(
|
|
532
|
+
path.join(routeSet.runtime.sourceRoot,'package.json'),'utf8'
|
|
533
|
+
)).version;
|
|
534
|
+
}
|
|
535
|
+
let assetVersion=await selectedAssetVersion();
|
|
512
536
|
for(const mapping of mappings){
|
|
513
537
|
const info=await lstat(mapping.root);
|
|
514
538
|
if(info.isSymbolicLink()||!info.isDirectory())fail(`Server route root must be a real directory: ${mapping.root}.`);
|
|
515
539
|
mapping.root=await realpath(mapping.root);
|
|
516
540
|
}
|
|
541
|
+
// Remember actual resource edges as their owners are served, not every
|
|
542
|
+
// HTML/JS/CSS file in an application's document or attachment corpus.
|
|
543
|
+
const resourcePaths=new Set([routeSet.startPath]);
|
|
517
544
|
const requestTasks=new Set();
|
|
518
545
|
const runFileWork=createFileWorkLimiter();
|
|
519
546
|
const server=http.createServer((request,response)=>{
|
|
@@ -541,7 +568,47 @@ async function startOwnedDevServer({
|
|
|
541
568
|
await runFileWork(async()=>{
|
|
542
569
|
const opened=await openSafeFile(mapping.root,relative);
|
|
543
570
|
if(!opened){deny(response,404,'Not found.');return;}
|
|
544
|
-
|
|
571
|
+
const extension=path.extname(opened.candidate).toLowerCase();
|
|
572
|
+
const html=extension==='.html'||extension==='.htm';
|
|
573
|
+
let managedDocument=false;
|
|
574
|
+
if(html&&opened.content.includes(MANAGED_IMPORT_MAP_ATTRIBUTE)){
|
|
575
|
+
try{
|
|
576
|
+
managedDocument=inspectImportMapHtml(
|
|
577
|
+
opened.content.toString('utf8')
|
|
578
|
+
).managedMaps.length>0;
|
|
579
|
+
}catch{
|
|
580
|
+
// An unrelated document is not a source validation surface.
|
|
581
|
+
}
|
|
582
|
+
}
|
|
583
|
+
const entryDocument=target.path===routeSet.startPath||managedDocument;
|
|
584
|
+
const managedMap=path.basename(opened.candidate)==='arcane.importmap.json';
|
|
585
|
+
// A live server can span an SDK upgrade. Refresh the small
|
|
586
|
+
// version record on navigation, not on each resource request.
|
|
587
|
+
if(entryDocument||managedMap)assetVersion=await selectedAssetVersion();
|
|
588
|
+
const runtimeResource=segments[0]==='arcane';
|
|
589
|
+
const browserResource=['script','style','worker','sharedworker','serviceworker']
|
|
590
|
+
.includes(request.headers['sec-fetch-dest']);
|
|
591
|
+
const rewrite=runtimeResource||entryDocument||browserResource
|
|
592
|
+
||resourcePaths.has(target.path);
|
|
593
|
+
const onReference=({url,kind,baseHref,baseKind})=>{
|
|
594
|
+
if(baseKind==='document'||kind==='fetch'||(kind==='asset'&&!/\.css(?:[?#]|$)/iu.test(url))
|
|
595
|
+
||(kind==='import'&&!/^(?:\.{1,2}\/|\/)/u.test(url)))return;
|
|
596
|
+
try{
|
|
597
|
+
const documentUrl=new URL(target.path,'http://arcane.invalid');
|
|
598
|
+
const base=baseHref?new URL(baseHref,documentUrl):documentUrl;
|
|
599
|
+
const resource=new URL(url,base);
|
|
600
|
+
if(resource.origin===documentUrl.origin
|
|
601
|
+
&&/\.(?:m?js|html?|css)$/iu.test(resource.pathname)){
|
|
602
|
+
resourcePaths.add(decodeURIComponent(resource.pathname));
|
|
603
|
+
}
|
|
604
|
+
}catch{ /* Non-URL values remain under their existing owner. */ }
|
|
605
|
+
};
|
|
606
|
+
await sendFile(response,opened,{
|
|
607
|
+
head:request.method==='HEAD',
|
|
608
|
+
assetVersion:rewrite?assetVersion:undefined,
|
|
609
|
+
revalidate:entryDocument||managedMap,
|
|
610
|
+
onReference
|
|
611
|
+
});
|
|
545
612
|
});
|
|
546
613
|
})().catch(async error=>{
|
|
547
614
|
await events.enqueue({type:'server.request.failed',message:error.message});
|