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
@@ -1,23 +1,9 @@
1
- import {randomBytes,timingSafeEqual} from 'node:crypto';
2
1
  import {constants as FS_CONSTANTS} from 'node:fs';
3
2
  import {lstat,open,realpath} from 'node:fs/promises';
4
3
  import http from 'node:http';
5
4
  import path from 'node:path';
6
- import {
7
- authenticateAppReleaseReceipt,
8
- readVerifiedAppReleaseFile,
9
- RELEASE_MANIFEST_NAME
10
- } from './packager/core.mjs';
11
- import {
12
- authenticateWorkspaceRuntimeReceipt,
13
- readVerifiedWorkspaceRuntimeFile,
14
- verifyWorkspaceRuntime
15
- } from './workspace-runtime.mjs';
16
- import {verifyRuntime} from './runtime.mjs';
17
- import {verifySdkBrowserRuntime} from './sdk-browser-runtime.mjs';
18
- import {resolveWorkspace,validateWorkspace} from './workspace.mjs';
5
+ import {resolveWorkspace} from './workspace.mjs';
19
6
  import {createEventQueue} from './event-queue.mjs';
20
- import {SDK_NAME,SDK_VERSION} from './constants.mjs';
21
7
 
22
8
  const MIME_TYPES=new Map([
23
9
  ['.css','text/css; charset=utf-8'],
@@ -38,10 +24,6 @@ const MIME_TYPES=new Map([
38
24
  ['.woff2','font/woff2']
39
25
  ]);
40
26
  const READ_ONLY_NO_FOLLOW=FS_CONSTANTS.O_RDONLY|(FS_CONSTANTS.O_NOFOLLOW??0);
41
- const MAX_DEVELOPMENT_FILE_BYTES=64*1024*1024;
42
- const MAX_CONCURRENT_FILE_RESPONSES=4;
43
- const MAX_PENDING_FILE_RESPONSES=256;
44
- const SESSION_COOKIE='Arcane-Dev-Session';
45
27
  const PRIVATE_SOURCE_SEGMENTS=new Set([
46
28
  'arcane-app.json','arcane-package.json','test','tests','scripts','node_modules','dist','local'
47
29
  ]);
@@ -57,27 +39,6 @@ const SDK_RUNTIME_SOURCE_PRIVATE_MANIFESTS=new Set([
57
39
  'arcane_app_release.json','arcane_runtime_release.json','arcane_sdk_browser_release.json'
58
40
  ]);
59
41
 
60
- // This server is a loopback-only development host, not a production policy
61
- // boundary. The bundled runtime currently needs inline component execution,
62
- // workers, remote provider requests, media, and embedded web content. The
63
- // unguessable session cookie and exact numeric Host check protect this broad
64
- // development CSP from being exposed as a general network service.
65
- const DEVELOPMENT_CSP=[
66
- "default-src 'self'",
67
- "script-src 'self' 'unsafe-inline' 'wasm-unsafe-eval'",
68
- "style-src 'self' 'unsafe-inline'",
69
- "img-src 'self' data: blob: http: https:",
70
- "font-src 'self' data:",
71
- "connect-src 'self' data: blob: http: https: ws: wss:",
72
- "worker-src 'self' blob:",
73
- "frame-src 'self' data: blob: http: https:",
74
- "media-src 'self' data: blob: http: https:",
75
- "object-src 'none'",
76
- "frame-ancestors 'none'",
77
- "base-uri 'self'",
78
- "form-action 'self'"
79
- ].join('; ');
80
-
81
42
  function fail(message,code='ARCANE_OPERATION_FAILED'){
82
43
  const error=new Error(message);
83
44
  error.code=code;
@@ -91,20 +52,9 @@ function throwIfAborted(signal){
91
52
  throw error;
92
53
  }
93
54
 
94
- function responseSecurityHeaders(contentSecurityPolicy=DEVELOPMENT_CSP){
95
- return {
96
- 'cache-control':'no-store',
97
- 'x-content-type-options':'nosniff',
98
- 'content-security-policy':contentSecurityPolicy,
99
- 'cross-origin-resource-policy':'same-origin',
100
- 'referrer-policy':'no-referrer'
101
- };
102
- }
103
-
104
55
  function deny(response,status,message){
105
56
  response.writeHead(status,{
106
- 'content-type':'text/plain; charset=utf-8',
107
- ...responseSecurityHeaders("default-src 'none'; frame-ancestors 'none'; base-uri 'none'")
57
+ 'content-type':'text/plain; charset=utf-8'
108
58
  });
109
59
  response.end(`${message}\n`);
110
60
  }
@@ -136,17 +86,6 @@ function resolveInside(root,segments){
136
86
  return candidate;
137
87
  }
138
88
 
139
- function identityMatches(info,identity){
140
- return !identity||(
141
- String(info.dev)===identity.device
142
- &&String(info.ino)===identity.inode
143
- &&Number(info.size)===identity.bytes
144
- &&String(info.mtimeNs)===identity.modifiedNanoseconds
145
- &&String(info.ctimeNs)===identity.changedNanoseconds
146
- &&(identity.links===undefined||String(info.nlink)===identity.links)
147
- );
148
- }
149
-
150
89
  function inventoryKey(value){
151
90
  return process.platform==='win32'?value.toLowerCase():value;
152
91
  }
@@ -235,35 +174,6 @@ async function verifySdkRuntimeSourceRoot(sourceRoot,workspaceRoot,appId,{signal
235
174
  'ARCANE_DEV_RUNTIME_SOURCE_INVALID');
236
175
  }
237
176
 
238
- let packageFile;
239
- try{
240
- packageFile=await openSafeFile(canonicalRoot,['package.json']);
241
- }catch{
242
- fail('SDK runtime source package.json must be a readable bounded real file.',
243
- 'ARCANE_DEV_RUNTIME_SOURCE_INVALID');
244
- }
245
- if(!packageFile){
246
- fail('SDK runtime source root must contain a real package.json.',
247
- 'ARCANE_DEV_RUNTIME_SOURCE_INVALID');
248
- }
249
- let packageDocument;
250
- try{
251
- packageDocument=JSON.parse(packageFile.bytes.toString('utf8'));
252
- }catch{
253
- fail('SDK runtime source package.json must be valid JSON.',
254
- 'ARCANE_DEV_RUNTIME_SOURCE_INVALID');
255
- }
256
- if(!packageDocument||Array.isArray(packageDocument)||packageDocument.name!==SDK_NAME){
257
- fail(`SDK runtime source package name must be exactly ${SDK_NAME}.`,
258
- 'ARCANE_DEV_RUNTIME_SOURCE_INVALID');
259
- }
260
- if(packageDocument.version!==SDK_VERSION){
261
- fail(
262
- `SDK runtime source version must exactly match the executing SDK (${SDK_VERSION}).`,
263
- 'ARCANE_DEV_RUNTIME_VERSION_MISMATCH'
264
- );
265
- }
266
-
267
177
  const roots=[
268
178
  {
269
179
  path:'runtime/arcane',
@@ -299,36 +209,23 @@ async function verifySdkRuntimeSourceRoot(sourceRoot,workspaceRoot,appId,{signal
299
209
  path:root.path
300
210
  });
301
211
  }
302
- const runtime=Object.freeze({
212
+ const runtime={
303
213
  mode:'sdk-source',
304
214
  protocol:SDK_RUNTIME_SOURCE_PROTOCOL,
305
- sdkVersion:SDK_VERSION,
306
215
  mutable:true,
307
216
  distributionAuthority:false,
308
217
  sourceRoot:canonicalRoot
309
- });
218
+ };
310
219
  await emitRuntimeSourceEvent(onEvent,{
311
220
  type:'runtime.source.mount.ready',
312
221
  appId,
313
222
  canonicalRoot,
314
- sdkVersion:SDK_VERSION,
315
223
  protocol:SDK_RUNTIME_SOURCE_PROTOCOL,
316
224
  routeCount:mappings.length
317
225
  });
318
226
  return {mappings,runtime};
319
227
  }
320
228
 
321
- function identityMap(identities,prefix=''){
322
- const normalizedPrefix=prefix?`${prefix}/`:'';
323
- const result=new Map();
324
- for(const identity of identities||[]){
325
- if(!identity.path.startsWith(normalizedPrefix))continue;
326
- const relative=identity.path.slice(normalizedPrefix.length);
327
- if(relative)result.set(inventoryKey(relative),identity);
328
- }
329
- return result;
330
- }
331
-
332
229
  function routePrefixKey(prefix){
333
230
  return prefix.map(segment=>segment.normalize('NFC').toLowerCase()).join('/');
334
231
  }
@@ -355,38 +252,17 @@ function deterministicMappings(mappings){
355
252
  }
356
253
 
357
254
  function createFileWorkLimiter(){
358
- let active=0;
359
- const pending=[];
360
- const release=()=>{
361
- const next=pending.shift();
362
- if(next)next();
363
- else active-=1;
364
- };
365
- return async work=>{
366
- if(active>=MAX_CONCURRENT_FILE_RESPONSES){
367
- if(pending.length>=MAX_PENDING_FILE_RESPONSES){
368
- fail('Development server file queue is full.','ARCANE_BACKPRESSURE');
369
- }
370
- await new Promise(resolve=>pending.push(resolve));
371
- }else{
372
- active+=1;
373
- }
374
- try{
375
- return await work();
376
- }finally{
377
- release();
378
- }
379
- };
255
+ return async work=>work();
380
256
  }
381
257
 
382
- async function openSafeFile(root,segments,expectedIdentity){
258
+ async function openSafeFile(root,segments){
383
259
  const candidate=resolveInside(root,segments);
384
260
  if(!candidate)return null;
385
261
  let current=root;
386
262
  for(const segment of segments){
387
263
  current=path.join(current,segment);
388
264
  let info;
389
- try{info=await lstat(current,{bigint:true});}
265
+ try{info=await lstat(current);}
390
266
  catch(error){
391
267
  if(error?.code==='ENOENT')return null;
392
268
  throw error;
@@ -394,8 +270,10 @@ async function openSafeFile(root,segments,expectedIdentity){
394
270
  if(info.isSymbolicLink())return null;
395
271
  }
396
272
 
397
- const before=await lstat(candidate,{bigint:true});
398
- if(before.isSymbolicLink()||!before.isFile()||!identityMatches(before,expectedIdentity))return null;
273
+ const currentInfo=await lstat(candidate);
274
+ if(currentInfo.isSymbolicLink()||!currentInfo.isFile())return null;
275
+ const canonicalCandidate=await realpath(candidate);
276
+ if(!pathIsWithin(root,canonicalCandidate))return null;
399
277
  let handle;
400
278
  try{
401
279
  handle=await open(candidate,READ_ONLY_NO_FOLLOW);
@@ -404,53 +282,15 @@ async function openSafeFile(root,segments,expectedIdentity){
404
282
  throw error;
405
283
  }
406
284
  try{
407
- const opened=await handle.stat({bigint:true});
408
- if(!opened.isFile()||!identityMatches(opened,expectedIdentity)
409
- ||!identityMatches(opened,{
410
- device:String(before.dev),
411
- inode:String(before.ino),
412
- bytes:Number(before.size),
413
- modifiedNanoseconds:String(before.mtimeNs),
414
- changedNanoseconds:String(before.ctimeNs),
415
- links:String(before.nlink)
416
- })){
417
- return null;
418
- }
419
- if(Number(opened.size)>MAX_DEVELOPMENT_FILE_BYTES){
420
- fail(
421
- `Development file exceeds the ${MAX_DEVELOPMENT_FILE_BYTES}-byte serving limit.`,
422
- 'ARCANE_POLICY_DENIED'
423
- );
424
- }
425
- const bytes=await handle.readFile();
426
- const after=await handle.stat({bigint:true});
427
- if(!identityMatches(after,{
428
- device:String(opened.dev),
429
- inode:String(opened.ino),
430
- bytes:Number(opened.size),
431
- modifiedNanoseconds:String(opened.mtimeNs),
432
- changedNanoseconds:String(opened.ctimeNs),
433
- links:String(opened.nlink)
434
- })||bytes.length!==Number(opened.size)){
435
- return null;
436
- }
437
- const canonicalCandidate=await realpath(candidate);
438
- const relative=path.relative(root,canonicalCandidate);
439
- if(relative.startsWith('..')||path.isAbsolute(relative)){
285
+ const opened=await handle.stat();
286
+ if(!opened.isFile())return null;
287
+ const content=await handle.readFile();
288
+ const servedCandidate=await realpath(candidate);
289
+ if(!pathIsWithin(root,servedCandidate)
290
+ ||canonicalLocationKey(servedCandidate)!==canonicalLocationKey(canonicalCandidate)){
440
291
  return null;
441
292
  }
442
- const currentInfo=await lstat(candidate,{bigint:true});
443
- if(!identityMatches(currentInfo,{
444
- device:String(opened.dev),
445
- inode:String(opened.ino),
446
- bytes:Number(opened.size),
447
- modifiedNanoseconds:String(opened.mtimeNs),
448
- changedNanoseconds:String(opened.ctimeNs),
449
- links:String(opened.nlink)
450
- })){
451
- return null;
452
- }
453
- return {candidate:canonicalCandidate,bytes,size:bytes.length};
293
+ return {candidate:servedCandidate,content};
454
294
  }catch(error){
455
295
  if(error?.code==='ENOENT')return null;
456
296
  throw error;
@@ -463,8 +303,7 @@ async function sendFile(response,opened,{head=false}={}){
463
303
  const extension=path.extname(opened.candidate).toLowerCase();
464
304
  response.writeHead(200,{
465
305
  'content-type':MIME_TYPES.get(extension)||'application/octet-stream',
466
- 'content-length':opened.size,
467
- ...responseSecurityHeaders()
306
+ 'content-length':opened.content.byteLength
468
307
  });
469
308
  await new Promise((resolve,reject)=>{
470
309
  let settled=false;
@@ -488,7 +327,7 @@ async function sendFile(response,opened,{head=false}={}){
488
327
  response.once('error',failed);
489
328
  response.once('finish',completed);
490
329
  response.once('close',completed);
491
- response.end(head?undefined:opened.bytes);
330
+ response.end(head?undefined:opened.content);
492
331
  });
493
332
  }
494
333
 
@@ -526,7 +365,6 @@ function sharedPathAllowed(relative,route){
526
365
  }
527
366
 
528
367
  async function sourceRoutes(workspaceRoot,appId,{
529
- workspaceRuntimeReceipt,
530
368
  sdkRuntimeSourceRoot,
531
369
  signal,
532
370
  onEvent
@@ -570,58 +408,6 @@ async function sourceRoutes(workspaceRoot,appId,{
570
408
  };
571
409
  }
572
410
  const runtimeRoot=path.join(resolved.workspaceRoot,'arcane');
573
- const validation=await validateWorkspace({
574
- workspaceRoot:resolved.workspaceRoot,
575
- appId:resolved.appId,
576
- allowMissingManagedImportMap:true,
577
- signal
578
- });
579
- const sdkInstallation=validation.sdkInstallation;
580
- if(!sdkInstallation
581
- ||typeof sdkInstallation.runtimeRoot!=='string'
582
- ||typeof sdkInstallation.browserRuntimeRoot!=='string'){
583
- fail(
584
- 'Validated external workspace is missing its bound SDK installation authority.',
585
- 'ARCANE_WORKSPACE_INVALID'
586
- );
587
- }
588
- let verified=workspaceRuntimeReceipt;
589
- if(!verified){
590
- const sdkRuntimeRoot=sdkInstallation.runtimeRoot;
591
- const sdkRuntimeReceipt=await verifyRuntime({
592
- runtimeRoot:sdkRuntimeRoot,
593
- signal
594
- });
595
- const sdkBrowserRuntimeRoot=sdkInstallation.browserRuntimeRoot;
596
- const sdkBrowserRuntimeReceipt=await verifySdkBrowserRuntime({
597
- browserRuntimeRoot:sdkBrowserRuntimeRoot,
598
- signal
599
- });
600
- verified=await verifyWorkspaceRuntime({
601
- workspaceRoot:resolved.workspaceRoot,
602
- runtimeRoot:sdkRuntimeRoot,
603
- runtimeReceipt:sdkRuntimeReceipt,
604
- browserRuntimeRoot:sdkBrowserRuntimeRoot,
605
- sdkBrowserRuntimeReceipt,
606
- signal
607
- });
608
- }
609
- verified=await authenticateWorkspaceRuntimeReceipt(verified,{
610
- workspaceRoot:resolved.workspaceRoot,
611
- signal
612
- });
613
- if(typeof verified.sourceRuntimeLocation!=='string'
614
- ||typeof verified.sourceBrowserRuntimeLocation!=='string'
615
- ||canonicalLocationKey(verified.sourceRuntimeLocation)
616
- !==canonicalLocationKey(sdkInstallation.runtimeRoot)
617
- ||canonicalLocationKey(verified.sourceBrowserRuntimeLocation)
618
- !==canonicalLocationKey(sdkInstallation.browserRuntimeRoot)){
619
- fail(
620
- 'Workspace runtime receipt sources do not match the bound SDK installation authority.',
621
- 'ARCANE_INTEGRITY_FAILED'
622
- );
623
- }
624
- const arcaneIdentities=identityMap(verified.identities);
625
411
  return {
626
412
  workspaceRoot:resolved.workspaceRoot,
627
413
  workspaceMode:'external',
@@ -632,61 +418,27 @@ async function sourceRoutes(workspaceRoot,appId,{
632
418
  {
633
419
  prefix:['arcane'],
634
420
  root:runtimeRoot,
635
- identities:arcaneIdentities,
636
- read:relative=>readVerifiedWorkspaceRuntimeFile(verified,{
637
- workspaceRoot:resolved.workspaceRoot,
638
- relativePath:relative.join('/'),
639
- signal
640
- }),
641
- allow:relative=>arcaneIdentities.has(inventoryKey(relative.join('/')))
421
+ allow:sdkArcaneSourcePathAllowed
642
422
  }
643
423
  ]
644
424
  };
645
425
  }
646
426
 
647
- async function packagedRoutes(releaseRoot,releaseReceipt,{signal}={}){
427
+ async function packagedRoutes(releaseRoot){
648
428
  if(typeof releaseRoot!=='string'||!releaseRoot.trim())fail('releaseRoot is required in packaged mode.','ARCANE_USAGE');
649
- if(!releaseReceipt)fail('An authenticated release receipt is required in packaged mode.','ARCANE_POLICY_DENIED');
650
429
  const requested=path.resolve(releaseRoot);
651
- await authenticateAppReleaseReceipt(releaseReceipt,{releaseRoot:requested,signal});
652
- const info=await lstat(requested);
653
- if(info.isSymbolicLink()||!info.isDirectory())fail('Packaged release root must be a real directory.');
654
- const canonical=await realpath(requested);
655
- const identities=identityMap(releaseReceipt.identities);
430
+ const canonical=await canonicalRealDirectory(requested,'Packaged release root');
656
431
  return {
657
432
  workspaceRoot:null,
658
433
  appId:null,
659
434
  startPath:'/index.html',
660
435
  mappings:[{
661
436
  prefix:[],
662
- root:canonical,
663
- identities,
664
- read:relative=>readVerifiedAppReleaseFile(releaseReceipt,{
665
- releaseRoot:canonical,
666
- relativePath:relative.join('/'),
667
- signal
668
- }),
669
- allow:relative=>identities.has(inventoryKey(relative.join('/')))
437
+ root:canonical
670
438
  }]
671
439
  };
672
440
  }
673
441
 
674
- function tokenMatches(value,expected){
675
- if(typeof value!=='string')return false;
676
- const received=Buffer.from(value,'utf8');
677
- const wanted=Buffer.from(expected,'utf8');
678
- return received.length===wanted.length&&timingSafeEqual(received,wanted);
679
- }
680
-
681
- function hasSessionCookie(request,cookieName,sessionToken){
682
- const matches=String(request.headers.cookie||'')
683
- .split(';')
684
- .map(part=>part.trim())
685
- .filter(part=>part.startsWith(`${cookieName}=`))
686
- .map(part=>part.slice(cookieName.length+1));
687
- return matches.length===1&&tokenMatches(matches[0],sessionToken);
688
- }
689
-
690
442
  function listen(server,{host,port,signal}){
691
443
  return new Promise((resolve,reject)=>{
692
444
  const cleanup=()=>{
@@ -718,9 +470,7 @@ async function startOwnedDevServer({
718
470
  host='127.0.0.1',
719
471
  port=0,
720
472
  signal,
721
- workspaceRuntimeReceipt,
722
- sdkRuntimeSourceRoot,
723
- releaseReceipt
473
+ sdkRuntimeSourceRoot
724
474
  }={},events,releaseSignal){
725
475
  throwIfAborted(signal);
726
476
  if(mode!=='source'&&mode!=='packaged')fail(`Unsupported server mode: ${String(mode)}.`,'ARCANE_USAGE');
@@ -744,65 +494,31 @@ async function startOwnedDevServer({
744
494
  });
745
495
  const routeSet=mode==='source'
746
496
  ?await sourceRoutes(workspaceRoot,appId,{
747
- workspaceRuntimeReceipt,
748
497
  sdkRuntimeSourceRoot,
749
498
  signal,
750
499
  onEvent:event=>events.send(event)
751
500
  })
752
- :await packagedRoutes(releaseRoot,releaseReceipt,{signal});
501
+ :await packagedRoutes(releaseRoot);
753
502
  const mappings=deterministicMappings(routeSet.mappings);
754
503
  for(const mapping of mappings){
755
504
  const info=await lstat(mapping.root);
756
505
  if(info.isSymbolicLink()||!info.isDirectory())fail(`Server route root must be a real directory: ${mapping.root}.`);
757
506
  mapping.root=await realpath(mapping.root);
758
507
  }
759
- const sessionToken=randomBytes(32).toString('hex');
760
- const sessionCookieName=`${SESSION_COOKIE}-${randomBytes(8).toString('hex')}`;
761
- let expectedAuthority=null;
762
508
  const requestTasks=new Set();
763
509
  const runFileWork=createFileWorkLimiter();
764
510
  const server=http.createServer((request,response)=>{
765
511
  let task;
766
512
  task=(async()=>{
767
- if(!expectedAuthority){deny(response,503,'Server is starting.');return;}
768
- if(request.headers.host!==expectedAuthority){
769
- deny(response,421,'Misdirected request.');
770
- return;
771
- }
772
513
  if(request.method!=='GET'&&request.method!=='HEAD'){
773
514
  deny(response,405,'Method not allowed.');
774
515
  return;
775
516
  }
776
517
  const target=parseRequestTarget(request.url);
777
518
  if(!target){deny(response,400,'Invalid request path.');return;}
778
- const queryKeys=[...target.searchParams.keys()];
779
- const isBootstrap=request.method==='GET'
780
- &&target.path===routeSet.startPath
781
- &&queryKeys.length===1
782
- &&queryKeys[0]==='arcane_session'
783
- &&tokenMatches(target.searchParams.get('arcane_session'),sessionToken);
784
- if(isBootstrap){
785
- response.writeHead(302,{
786
- location:routeSet.startPath,
787
- 'set-cookie':`${sessionCookieName}=${sessionToken}; HttpOnly; SameSite=Strict; Path=/`,
788
- ...responseSecurityHeaders()
789
- });
790
- response.end();
791
- return;
792
- }
793
- if(!hasSessionCookie(request,sessionCookieName,sessionToken)){
794
- deny(response,403,'Development server session required.');
795
- return;
796
- }
797
519
  const {segments}=target;
798
- if(mode==='packaged'&&segments.some(segment=>
799
- segment.toLowerCase()===RELEASE_MANIFEST_NAME.toLowerCase()
800
- )){
801
- deny(response,404,'Not found.');
802
- return;
803
- }
804
520
  if(segments.length===0){
805
- response.writeHead(302,{location:routeSet.startPath,...responseSecurityHeaders()});
521
+ response.writeHead(302,{location:routeSet.startPath});
806
522
  response.end();
807
523
  return;
808
524
  }
@@ -814,23 +530,14 @@ async function startOwnedDevServer({
814
530
  if(relative.length===0){deny(response,404,'Not found.');return;}
815
531
  if(mapping.allow&&!mapping.allow(relative)){deny(response,404,'Not found.');return;}
816
532
  await runFileWork(async()=>{
817
- const expectedIdentity=mapping.identities?.get(inventoryKey(relative.join('/')));
818
- const opened=mapping.read
819
- ?{
820
- candidate:path.join(mapping.root,...relative),
821
- bytes:await mapping.read(relative)
822
- }
823
- :await openSafeFile(mapping.root,relative,expectedIdentity);
533
+ const opened=await openSafeFile(mapping.root,relative);
824
534
  if(!opened){deny(response,404,'Not found.');return;}
825
- opened.size=opened.bytes.length;
826
535
  await sendFile(response,opened,{head:request.method==='HEAD'});
827
536
  });
828
537
  })().catch(async error=>{
829
538
  await events.enqueue({type:'server.request.failed',message:error.message});
830
539
  if(!response.headersSent){
831
- const status=error?.code==='ARCANE_POLICY_DENIED'?413
832
- :error?.code==='ARCANE_BACKPRESSURE'?503
833
- :500;
540
+ const status=error?.code==='ARCANE_BACKPRESSURE'?503:500;
834
541
  deny(response,status,'Internal server error.');
835
542
  }
836
543
  else response.destroy(error);
@@ -847,10 +554,9 @@ async function startOwnedDevServer({
847
554
  }
848
555
  const visibleHost=address.family==='IPv6'?`[${address.address}]`:address.address;
849
556
  const endpoint=new URL(`http://${visibleHost}:${address.port}`);
850
- expectedAuthority=endpoint.host;
851
557
  const origin=endpoint.origin;
852
558
  const cleanUrl=`${origin}${routeSet.startPath}`;
853
- const url=`${cleanUrl}?arcane_session=${sessionToken}`;
559
+ const url=cleanUrl;
854
560
  let closeInitiated=false;
855
561
  let lifecycleSettlementStarted=false;
856
562
  let operationalError=null;