arcane-os 0.3.0 → 0.3.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (153) hide show
  1. package/CHANGELOG.md +27 -0
  2. package/README.md +86 -117
  3. package/bin/arcane-test.mjs +170 -46
  4. package/browser-runtime/ai/browser-speech-artifacts.mjs +887 -909
  5. package/browser-runtime/ai/browser-speech-providers.mjs +96 -152
  6. package/browser-runtime/ai/browser-wasm-llm-provider.mjs +627 -819
  7. package/browser-runtime/ai/browser-wasm.mjs +24 -35
  8. package/browser-runtime/ai/browser-wllama-runtime.mjs +64 -316
  9. package/browser-runtime/ai/model-controller.mjs +584 -181
  10. package/browser-runtime/ai/speech-worker-client.mjs +8 -146
  11. package/browser-runtime/ai/speech-worker-runtime.mjs +643 -363
  12. package/browser-runtime/dom-event-instrumentation.mjs +55 -147
  13. package/browser-runtime/event-manager.mjs +239 -624
  14. package/package.json +5 -6
  15. package/runtime/arcane/components/app-bar.html +3 -15
  16. package/runtime/arcane/components/assistant-panel.html +10 -10
  17. package/runtime/arcane/components/calculator.html +1 -1
  18. package/runtime/arcane/components/chat.html +1359 -135
  19. package/runtime/arcane/components/conversation-view.html +2 -2
  20. package/runtime/arcane/components/document-inspector.html +11 -17
  21. package/runtime/arcane/components/file-manager.html +13 -56
  22. package/runtime/arcane/components/markdown-document.html +82 -281
  23. package/runtime/arcane/components/markdown-editor.html +7 -10
  24. package/runtime/arcane/components/media-embed.html +6 -6
  25. package/runtime/arcane/components/screen-capture.html +4 -4
  26. package/runtime/arcane/components/source-explanation.html +2 -2
  27. package/runtime/arcane/components/speech.html +112 -68
  28. package/runtime/arcane/components/terminal-workspace.html +4 -4
  29. package/runtime/arcane/components/theme-editor.html +1 -1
  30. package/runtime/arcane/components/unified-inbox.html +2 -2
  31. package/runtime/arcane/components/voice-transcription.html +31 -21
  32. package/runtime/arcane/entities/Calculation.js +2 -3
  33. package/runtime/arcane/entities/Chat.js +228 -43
  34. package/runtime/arcane/entities/Preference.js +3 -5
  35. package/runtime/arcane/entities/Weather.js +5 -5
  36. package/runtime/arcane/modules/AI.js +1050 -427
  37. package/runtime/arcane/modules/AIProviderRuntime.js +658 -363
  38. package/runtime/arcane/modules/AIResponseLength.js +9 -19
  39. package/runtime/arcane/modules/AIRuntimeState.js +109 -72
  40. package/runtime/arcane/modules/ArcaneNavigationPolicy.js +45 -32
  41. package/runtime/arcane/modules/BrowserTestSuite.js +78 -122
  42. package/runtime/arcane/modules/CalculatorEngine.js +9 -9
  43. package/runtime/arcane/modules/CommunicationAppController.js +3 -7
  44. package/runtime/arcane/modules/ComponentContracts.js +30 -32
  45. package/runtime/arcane/modules/ConfiguredAIChatSession.js +281 -230
  46. package/runtime/arcane/modules/ConversationActionItems.js +26 -59
  47. package/runtime/arcane/modules/ConversationClosingReport.js +34 -61
  48. package/runtime/arcane/modules/ConversationTimebox.js +27 -15
  49. package/runtime/arcane/modules/DBOPFSDocumentLibrary.js +152 -344
  50. package/runtime/arcane/modules/DocumentLexicalSearch.js +25 -91
  51. package/runtime/arcane/modules/HTMLImport.js +54 -1
  52. package/runtime/arcane/modules/IsolatedModelQuestionRunner.js +40 -203
  53. package/runtime/arcane/modules/LocalAIReadiness.js +40 -60
  54. package/runtime/arcane/modules/LocalAIReadinessController.js +15 -13
  55. package/runtime/arcane/modules/MD.js +1 -45
  56. package/runtime/arcane/modules/Mail.js +51 -103
  57. package/runtime/arcane/modules/MailOutbox.mjs +95 -193
  58. package/runtime/arcane/modules/MailTransport.mjs +36 -57
  59. package/runtime/arcane/modules/ModelDefinition.js +22 -106
  60. package/runtime/arcane/modules/OpenMeteoWeatherProvider.js +39 -101
  61. package/runtime/arcane/modules/PersistentAIChatSession.js +281 -18
  62. package/runtime/arcane/modules/PreferenceStore.js +102 -30
  63. package/runtime/arcane/modules/RiskSignalAnalyzer.js +8 -9
  64. package/runtime/arcane/modules/ScopedOPFSCache.js +7 -42
  65. package/runtime/arcane/modules/ScreenCapture.js +175 -128
  66. package/runtime/arcane/modules/SpeechPlayback.js +46 -149
  67. package/runtime/arcane/modules/StaticDocumentCatalog.js +173 -407
  68. package/runtime/arcane/modules/ToolCallRouter.js +25 -12
  69. package/runtime/arcane/modules/YouTubeMedia.js +6 -5
  70. package/schemas/arcane-app-bundle.schema.json +13 -78
  71. package/schemas/arcane-app.schema.json +9 -25
  72. package/schemas/arcane-lock.schema.json +18 -151
  73. package/schemas/arcane-package.schema.json +2 -16
  74. package/schemas/native-build-plan.schema.json +119 -122
  75. package/src/app-descriptor.mjs +75 -132
  76. package/src/application-tests.mjs +200 -0
  77. package/src/cli/main.mjs +27 -46
  78. package/src/constants.mjs +3 -4
  79. package/src/dev-server.mjs +30 -324
  80. package/src/doctor.mjs +92 -154
  81. package/src/dom-event-instrumentation.mjs +55 -147
  82. package/src/errors.mjs +2 -3
  83. package/src/event-manager.mjs +239 -624
  84. package/src/event-queue.mjs +3 -3
  85. package/src/import-map.mjs +273 -1028
  86. package/src/index.mjs +14 -16
  87. package/src/installed-sdk-runtime.mjs +40 -62
  88. package/src/integrated-provider-loader.mjs +53 -382
  89. package/src/mail-api.mjs +0 -2
  90. package/src/mail-server.mjs +224 -580
  91. package/src/mail.mjs +4 -10
  92. package/src/native-plan.mjs +163 -598
  93. package/src/native-provider-loader.mjs +104 -1063
  94. package/src/packager/core.mjs +485 -3229
  95. package/src/process.mjs +5 -10
  96. package/src/release-bundle.mjs +292 -2405
  97. package/src/runtime.mjs +76 -396
  98. package/src/scaffold.mjs +30 -80
  99. package/src/sdk-browser-runtime.mjs +70 -626
  100. package/src/source-server.mjs +588 -0
  101. package/src/targets/index.mjs +78 -188
  102. package/src/templates/workspace-template.mjs +19 -135
  103. package/src/testing-loader.mjs +164 -0
  104. package/src/testing.mjs +1 -1
  105. package/src/toolchain.mjs +131 -544
  106. package/src/update-check.mjs +26 -64
  107. package/src/workspace-operation-lock.mjs +139 -430
  108. package/src/workspace-runtime.mjs +112 -779
  109. package/src/workspace.mjs +40 -302
  110. package/browser-runtime/ARCANE_SDK_BROWSER_RELEASE.json +0 -218
  111. package/browser-runtime/ai/ARCANE_AI_BROWSER_SPEECH_COMPONENTS.json +0 -203
  112. package/browser-runtime/ai/ARCANE_AI_BROWSER_WASM_COMPONENTS.json +0 -80
  113. package/browser-runtime/ai/internal/sha256.mjs +0 -166
  114. package/docs/architecture.md +0 -344
  115. package/docs/compatibility.md +0 -36
  116. package/docs/event-manager.md +0 -294
  117. package/docs/platform-targets.md +0 -108
  118. package/docs/publishing.md +0 -201
  119. package/docs/reference/README.md +0 -187
  120. package/docs/reference/ai/browser-speech-package-authority.json +0 -835
  121. package/docs/reference/ai/browser-speech.md +0 -1252
  122. package/docs/reference/ai/browser-wasm.md +0 -530
  123. package/docs/reference/arcane-ollama.md +0 -288
  124. package/docs/reference/availability-and-normalization.md +0 -183
  125. package/docs/reference/behavioral-testing.md +0 -133
  126. package/docs/reference/cli.md +0 -779
  127. package/docs/reference/core/README.md +0 -62
  128. package/docs/reference/core/arcane-ai-contracts.md +0 -907
  129. package/docs/reference/core/arcane-api.md +0 -601
  130. package/docs/reference/core/arcane-entities.md +0 -65
  131. package/docs/reference/core/arcane-events.md +0 -134
  132. package/docs/reference/core/ollama-module.md +0 -181
  133. package/docs/reference/core/reference/arcane-api/ai-and-ollama.md +0 -1909
  134. package/docs/reference/core/reference/arcane-api/applications-terminal-capabilities.md +0 -1057
  135. package/docs/reference/core/reference/arcane-api/core-and-events.md +0 -320
  136. package/docs/reference/core/reference/arcane-api/filesystem-storage-preferences-appearance.md +0 -610
  137. package/docs/reference/core/reference/arcane-api/namespaces.md +0 -1157
  138. package/docs/reference/core/reference/arcane-api/platform-installation-users-system.md +0 -1423
  139. package/docs/reference/core/reference/arcane-api/session-provisioning-diagnostics-development.md +0 -315
  140. package/docs/reference/event-manager.md +0 -1511
  141. package/docs/reference/inventory/package-api.json +0 -3284
  142. package/docs/reference/inventory/runtime-components.json +0 -1011
  143. package/docs/reference/inventory/runtime-entities.json +0 -26
  144. package/docs/reference/inventory/runtime-modules.json +0 -1431
  145. package/docs/reference/mail.md +0 -316
  146. package/docs/reference/protocols.md +0 -677
  147. package/docs/reference/runtime-components.md +0 -1366
  148. package/docs/reference/runtime-entities.md +0 -303
  149. package/docs/reference/runtime-modules.md +0 -2960
  150. package/docs/reference/sdk-api.md +0 -6694
  151. package/docs/roadmap.md +0 -79
  152. package/docs/work-amplification.md +0 -129
  153. package/runtime/ARCANE_RUNTIME_RELEASE.json +0 -826
package/src/doctor.mjs CHANGED
@@ -1,35 +1,14 @@
1
1
  import path from 'node:path';
2
- import {access} from 'node:fs/promises';
3
- import {verifyRuntime} from './runtime.mjs';
4
- import {verifySdkBrowserRuntime} from './sdk-browser-runtime.mjs';
5
- import {verifyWorkspaceRuntime} from './workspace-runtime.mjs';
2
+ import {access,lstat,readdir,realpath} from 'node:fs/promises';
3
+ import {loadRuntimeRelease} from './runtime.mjs';
4
+ import {loadSdkBrowserRuntimeRelease} from './sdk-browser-runtime.mjs';
6
5
  import {validateWorkspace} from './workspace.mjs';
7
6
  import {runProcess} from './process.mjs';
8
7
  import {ARCANE_PROTOCOL,SDK_VERSION} from './constants.mjs';
9
8
  import {ERROR_CODES,ArcaneError,throwIfAborted} from './errors.mjs';
10
9
 
11
- const MINIMUM_NODE=[22,23,2];
12
10
  const WINDOWS_SERVICE_NAME='ArcaneOllama';
13
11
  const WINDOWS_SERVICE_HOST='C:\\Program Files\\Ollama\\ArcaneOllamaService.exe';
14
- const WINDOWS_SERVICE_COMMAND=`"${WINDOWS_SERVICE_HOST}"`;
15
- const OLLAMA_ENDPOINT='http://127.0.0.1:11434';
16
-
17
- function parseVersion(value){
18
- const match=String(value??'').match(/(\d+)\.(\d+)\.(\d+)/u);
19
- return match?match.slice(1).map(Number):null;
20
- }
21
-
22
- function versionAtLeast(actual,minimum){
23
- if(!actual){
24
- return false;
25
- }
26
- for(let index=0;index<minimum.length;index+=1){
27
- if((actual[index]??0)!==minimum[index]){
28
- return (actual[index]??0)>minimum[index];
29
- }
30
- }
31
- return true;
32
- }
33
12
 
34
13
  async function exists(filePath){
35
14
  try{
@@ -44,16 +23,15 @@ function check(id,status,message,{required=true,details}={}){
44
23
  return {id,status,message,required,...(details===undefined?{}:{details})};
45
24
  }
46
25
 
47
- async function commandCheck(id,command,args,{signal,onEvent,minimum,run=runProcess}={}){
26
+ async function commandCheck(id,command,args,{signal,onEvent,run=runProcess}={}){
48
27
  try{
49
28
  const result=await run(command,args,{signal,onEvent});
50
29
  const version=(result.stdout||result.stderr).trim();
51
- const supported=!minimum||versionAtLeast(parseVersion(version),minimum);
52
30
  return check(
53
31
  id,
54
- supported?'pass':'fail',
55
- supported?`${id} is available (${version}).`:`${id} ${version} is older than the supported minimum.`,
56
- {details:{version,minimum:minimum?.join('.')??null}}
32
+ 'pass',
33
+ `${id} is available (${version}).`,
34
+ {details:{version}}
57
35
  );
58
36
  }catch(error){
59
37
  return check(id,'fail',`${id} is unavailable: ${error.message}`,{
@@ -62,76 +40,58 @@ async function commandCheck(id,command,args,{signal,onEvent,minimum,run=runProce
62
40
  }
63
41
  }
64
42
 
65
- function parseServiceCommand(configOutput){
66
- const line=String(configOutput).split(/\r?\n/u)
67
- .find(value=>value.includes('BINARY_PATH_NAME'));
68
- if(!line){
69
- return {raw:null,binaryPath:null,arguments:null,argumentFree:false,exact:false};
70
- }
71
- const raw=line.slice(line.indexOf(':')+1).trim();
72
- const quoted=raw.match(/^"([^"]+)"(?:\s+(.+))?$/u);
73
- const unquoted=quoted?null:raw.match(/^(\S+?\.exe)(?:\s+(.+))?$/iu);
74
- const executable=quoted?.[1]??unquoted?.[1]??null;
75
- const argumentsText=quoted?.[2]??unquoted?.[2]??null;
76
- const binaryPath=executable?path.win32.normalize(executable):null;
77
- const argumentFree=Boolean(binaryPath)&&argumentsText===null;
78
- return {
79
- raw,
80
- binaryPath,
81
- arguments:argumentsText,
82
- argumentFree,
83
- exact:argumentFree&&raw.toLowerCase()===WINDOWS_SERVICE_COMMAND.toLowerCase()
84
- };
85
- }
86
-
87
- function serviceDefinition(configOutput,sidOutput){
88
- const text=String(configOutput);
89
- const command=parseServiceCommand(text);
90
- const ownProcess=/TYPE\s*:\s*10\s+WIN32_OWN_PROCESS/iu.test(text);
91
- const automatic=/START_TYPE\s*:\s*2\s+AUTO_START/iu.test(text);
92
- const localService=/SERVICE_START_NAME\s*:\s*(?:NT AUTHORITY\\)?LocalService\s*$/imu.test(text);
93
- const dependenciesLine=text.split(/\r?\n/u)
94
- .find(line=>/DEPENDENCIES\s*:/iu.test(line));
95
- const dependencies=dependenciesLine
96
- ?dependenciesLine.slice(dependenciesLine.indexOf(':')+1).trim()
97
- :null;
98
- const noUnexpectedDependencies=dependencies==='';
99
- const unrestrictedSid=/SERVICE_SID_TYPE\s*:\s*UNRESTRICTED/iu.test(String(sidOutput));
100
- return {
101
- binaryPath:command.binaryPath,
102
- command:command.raw,
103
- commandArguments:command.arguments,
104
- argumentFree:command.argumentFree,
105
- exactCommand:command.exact,
106
- ownProcess,
107
- automatic,
108
- localService,
109
- noUnexpectedDependencies,
110
- dependencies,
111
- unrestrictedSid
112
- };
113
- }
114
-
115
43
  function parseProbe(text){
116
44
  let value;
117
45
  try{
118
- value=JSON.parse(String(text).trim());
46
+ value=JSON.parse(String(text));
119
47
  }catch{
120
48
  return null;
121
49
  }
122
50
  if(!value||typeof value!=='object'||Array.isArray(value)
123
- ||Object.getPrototypeOf(value)!==Object.prototype
124
- ||JSON.stringify(Object.keys(value).sort())!==JSON.stringify(['endpoint','ready','service'])
125
- ||value.service!==WINDOWS_SERVICE_NAME
126
- ||value.ready!==true
127
- ||value.endpoint!==OLLAMA_ENDPOINT){
51
+ ||Object.getPrototypeOf(value)!==Object.prototype||typeof value.ready!=='boolean'){
128
52
  return null;
129
53
  }
130
- return Object.freeze({
131
- service:WINDOWS_SERVICE_NAME,
132
- ready:true,
133
- endpoint:OLLAMA_ENDPOINT
134
- });
54
+ return {...value};
55
+ }
56
+
57
+ function compareText(left,right){
58
+ const a=String(left);
59
+ const b=String(right);
60
+ return a<b?-1:a>b?1:0;
61
+ }
62
+
63
+ async function listPhysicalFiles(directory,label){
64
+ const requested=path.resolve(directory);
65
+ const rootInfo=await lstat(requested);
66
+ if(rootInfo.isSymbolicLink()||!rootInfo.isDirectory()){
67
+ throw new Error(`${label} must be a real directory.`);
68
+ }
69
+ const canonical=await realpath(requested);
70
+ const files=[];
71
+ async function visit(current,relativeRoot=''){
72
+ const entries=await readdir(current,{withFileTypes:true});
73
+ entries.sort((left,right)=>compareText(left.name,right.name));
74
+ for(const entry of entries){
75
+ const relative=relativeRoot?`${relativeRoot}/${entry.name}`:entry.name;
76
+ const absolute=path.join(current,entry.name);
77
+ const info=await lstat(absolute);
78
+ if(info.isSymbolicLink())throw new Error(`${label} contains a symbolic link: ${relative}.`);
79
+ if(info.isDirectory())await visit(absolute,relative);
80
+ else if(info.isFile())files.push(relative);
81
+ else throw new Error(`${label} contains a non-file entry: ${relative}.`);
82
+ }
83
+ }
84
+ await visit(canonical);
85
+ return files.sort(compareText);
86
+ }
87
+
88
+ function expectedWorkspaceRuntimeFiles(runtimeFiles,browserFiles){
89
+ return [
90
+ ...runtimeFiles.map(relative=>relative.startsWith('arcane/')
91
+ ?relative.slice('arcane/'.length)
92
+ :`dependencies/strong-type/${relative.slice('strong-type/'.length)}`),
93
+ ...browserFiles.map(relative=>`sdk/${relative}`)
94
+ ].sort(compareText);
135
95
  }
136
96
 
137
97
  async function assessWindowsOllama({
@@ -150,29 +110,12 @@ async function assessWindowsOllama({
150
110
  });
151
111
  }
152
112
 
153
- const configured=await run('sc.exe',['qc',WINDOWS_SERVICE_NAME],{
154
- signal,onEvent,allowNonzero:true
155
- });
156
- const sid=await run('sc.exe',['qsidtype',WINDOWS_SERVICE_NAME],{
157
- signal,onEvent,allowNonzero:true
158
- });
159
- const definition=serviceDefinition(configured.stdout,sid.stdout);
160
- const binaryPath=definition.binaryPath;
161
- const expectedPath=path.win32.normalize(WINDOWS_SERVICE_HOST);
162
- const registrationVerified=Boolean(configured.code===0
163
- &&sid.code===0
164
- &&binaryPath?.toLowerCase()===expectedPath.toLowerCase()
165
- &&definition.exactCommand
166
- &&definition.ownProcess
167
- &&definition.automatic
168
- &&definition.localService
169
- &&definition.noUnexpectedDependencies
170
- &&definition.unrestrictedSid);
171
113
  const running=/STATE\s*:\s*\d+\s+RUNNING/iu.test(queried.stdout);
172
114
  let probe=null;
173
115
  let probeExitCode=null;
116
+ const probeAvailable=await fileExists(WINDOWS_SERVICE_HOST);
174
117
 
175
- if(registrationVerified&&await fileExists(WINDOWS_SERVICE_HOST)){
118
+ if(probeAvailable){
176
119
  const probed=await run(WINDOWS_SERVICE_HOST,['--probe'],{
177
120
  signal,onEvent,allowNonzero:true
178
121
  });
@@ -180,24 +123,22 @@ async function assessWindowsOllama({
180
123
  probe=parseProbe(probed.stdout);
181
124
  }
182
125
 
183
- const probeVerified=probeExitCode===0&&probe!==null;
184
- const ready=running&&registrationVerified&&probeVerified;
126
+ const probeReady=probe===null?null:probeExitCode===0&&probe.ready===true;
127
+ const ready=running&&(probeReady??true);
185
128
  return check(
186
129
  'arcane-ollama',
187
130
  ready?'pass':'warning',
188
131
  ready
189
- ?`${WINDOWS_SERVICE_NAME} is registered and its managed wrapper reports ready.`
190
- :`${WINDOWS_SERVICE_NAME} is present but did not satisfy every managed-service readiness check.`,
132
+ ?`${WINDOWS_SERVICE_NAME} is running${probeReady===true?' and reports ready':''}.`
133
+ :`${WINDOWS_SERVICE_NAME} is present but is not ready.`,
191
134
  {
192
135
  required:false,
193
136
  details:{
194
137
  service:WINDOWS_SERVICE_NAME,
195
138
  running,
196
- registrationVerified,
197
- binaryPath,
198
- serviceDefinition:definition,
139
+ probeAvailable,
199
140
  probeExitCode,
200
- probeVerified,
141
+ probeReady,
201
142
  probe
202
143
  }
203
144
  }
@@ -236,33 +177,32 @@ export async function runDoctor({
236
177
  throwIfAborted(signal);
237
178
  const checks=[];
238
179
  const nodeVersion=process.versions.node;
239
- const nodeSupported=versionAtLeast(parseVersion(nodeVersion),MINIMUM_NODE);
240
180
  checks.push(check(
241
181
  'node',
242
- nodeSupported?'pass':'fail',
243
- nodeSupported
244
- ?`Node.js ${nodeVersion} satisfies the SDK minimum.`
245
- :`Node.js ${nodeVersion} does not satisfy the SDK minimum ${MINIMUM_NODE.join('.')}.`,
246
- {details:{version:nodeVersion,minimum:MINIMUM_NODE.join('.')}}
182
+ 'pass',
183
+ `Node.js ${nodeVersion} is available.`,
184
+ {details:{version:nodeVersion}}
247
185
  ));
248
186
 
249
187
  checks.push(await commandCheck('npm','npm',['--version'],{signal,onEvent,run}));
250
188
  checks.push(await commandCheck('git','git',['--version'],{signal,onEvent,run}));
251
189
 
252
190
  try{
253
- const [receipt,browserReceipt]=await Promise.all([
254
- verifyRuntime({signal,onEvent}),
255
- verifySdkBrowserRuntime({signal,onEvent})
191
+ const [runtimeRelease,browserRelease]=await Promise.all([
192
+ loadRuntimeRelease({signal}),
193
+ loadSdkBrowserRuntimeRelease({signal})
256
194
  ]);
257
- checks.push(check('sdk-runtime','pass','The packaged Arcane runtime inventory is verified.',{
195
+ checks.push(check('sdk-runtime','pass','The packaged Arcane runtime files are available.',{
258
196
  details:{
259
197
  sdkVersion:SDK_VERSION,
260
- contentSha256:receipt.contentSha256,
261
- browserContentSha256:browserReceipt.contentSha256
198
+ runtimeRoot:runtimeRelease.runtimeRoot,
199
+ browserRuntimeRoot:browserRelease.browserRuntimeRoot,
200
+ runtimeFiles:runtimeRelease.files,
201
+ browserRuntimeFiles:browserRelease.files
262
202
  }
263
203
  }));
264
204
  }catch(error){
265
- checks.push(check('sdk-runtime','fail',`The packaged Arcane runtime failed verification: ${error.message}`));
205
+ checks.push(check('sdk-runtime','fail',`The packaged Arcane runtime could not be read: ${error.message}`));
266
206
  }
267
207
 
268
208
  const resolvedWorkspace=path.resolve(workspaceRoot??process.cwd());
@@ -285,45 +225,43 @@ export async function runDoctor({
285
225
  checks.push(check(
286
226
  'workspace-runtime',
287
227
  'skipped',
288
- 'The physical workspace runtime was not checked because workspace admission failed.',
228
+ 'The physical workspace runtime was not checked because workspace validation failed.',
289
229
  {required:false,details:{workspaceRoot:resolvedWorkspace}}
290
230
  ));
291
231
  }else if(result.workspaceMode==='external'){
292
232
  try{
293
233
  const {runtimeRoot,browserRuntimeRoot}=result.sdkInstallation;
294
- const runtimeReceipt=await verifyRuntime({runtimeRoot,signal,onEvent});
295
- const sdkBrowserRuntimeReceipt=await verifySdkBrowserRuntime({
296
- browserRuntimeRoot,
297
- signal,
298
- onEvent
299
- });
300
- const projected=await verifyWorkspaceRuntime({
301
- workspaceRoot:result.workspaceRoot,
302
- runtimeRoot,
303
- runtimeReceipt,
304
- browserRuntimeRoot,
305
- sdkBrowserRuntimeReceipt,
306
- signal,
307
- onEvent
308
- });
234
+ const [runtimeRelease,browserRelease]=await Promise.all([
235
+ loadRuntimeRelease({runtimeRoot,signal}),
236
+ loadSdkBrowserRuntimeRelease({browserRuntimeRoot,signal})
237
+ ]);
238
+ const files=await listPhysicalFiles(
239
+ path.join(result.workspaceRoot,'arcane'),
240
+ 'Workspace Arcane runtime'
241
+ );
242
+ const expected=expectedWorkspaceRuntimeFiles(
243
+ runtimeRelease.files,
244
+ browserRelease.files
245
+ );
246
+ const present=new Set(files);
247
+ const missing=expected.filter(file=>!present.has(file));
248
+ if(missing.length){
249
+ throw new Error(`Workspace Arcane runtime is missing required SDK files: ${missing.join(', ')}`);
250
+ }
309
251
  checks.push(check(
310
252
  'workspace-runtime',
311
253
  'pass',
312
- 'The composed physical Arcane and SDK browser runtime is verified.',
254
+ 'The composed physical Arcane and SDK browser runtime is present.',
313
255
  {details:{
314
- contentSha256:projected.contentSha256,
315
- fileCount:projected.fileCount,
316
- runtimeManifestSha256:projected.sourceManifestSha256,
317
- runtimeContentSha256:projected.sourceContentSha256,
318
- browserManifestSha256:projected.sourceBrowserManifestSha256,
319
- browserContentSha256:projected.sourceBrowserContentSha256
256
+ layout:'external',
257
+ files
320
258
  }}
321
259
  ));
322
260
  }catch(error){
323
261
  checks.push(check(
324
262
  'workspace-runtime',
325
263
  'fail',
326
- `The composed physical workspace runtime is invalid: ${error.message}`
264
+ `The composed physical workspace runtime could not be read: ${error.message}`
327
265
  ));
328
266
  }
329
267
  }else if(result.workspaceMode==='integrated'){