arcane-os 0.1.2 → 0.2.1

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 (54) hide show
  1. package/CHANGELOG.md +35 -0
  2. package/NOTICE +5 -3
  3. package/README.md +73 -24
  4. package/browser-runtime/ARCANE_SDK_BROWSER_RELEASE.json +67 -18
  5. package/browser-runtime/ai/ARCANE_AI_BROWSER_WASM_COMPONENTS.json +16 -5
  6. package/browser-runtime/ai/browser-kokoro-worker.mjs +3 -0
  7. package/browser-runtime/ai/browser-speech-artifacts.mjs +1108 -0
  8. package/browser-runtime/ai/browser-speech-providers.mjs +780 -0
  9. package/browser-runtime/ai/browser-speech.mjs +9 -0
  10. package/browser-runtime/ai/browser-wasm-llm-provider.mjs +1537 -167
  11. package/browser-runtime/ai/browser-wasm.mjs +46 -1
  12. package/browser-runtime/ai/browser-whisper-worker.mjs +3 -0
  13. package/browser-runtime/ai/browser-wllama-runtime.mjs +677 -132
  14. package/browser-runtime/ai/model-controller.mjs +138 -12
  15. package/browser-runtime/ai/speech-worker-client.mjs +207 -0
  16. package/browser-runtime/ai/speech-worker-runtime.mjs +516 -0
  17. package/browser-runtime/ai/wllama/index.mjs +389 -0
  18. package/docs/architecture.md +132 -22
  19. package/docs/reference/README.md +1 -1
  20. package/docs/reference/ai/browser-wasm.md +101 -42
  21. package/docs/reference/availability-and-normalization.md +19 -5
  22. package/docs/reference/behavioral-testing.md +18 -5
  23. package/docs/reference/cli.md +2 -2
  24. package/docs/reference/inventory/package-api.json +14 -14
  25. package/docs/reference/protocols.md +4 -4
  26. package/docs/reference/sdk-api.md +68 -38
  27. package/docs/work-amplification.md +8 -4
  28. package/package.json +7 -3
  29. package/runtime/ARCANE_RUNTIME_RELEASE.json +50 -20
  30. package/runtime/arcane/components/chat.html +551 -62
  31. package/runtime/arcane/components/speech.html +1113 -265
  32. package/runtime/arcane/entities/Chat.js +246 -43
  33. package/runtime/arcane/modules/AI.js +1394 -162
  34. package/runtime/arcane/modules/AIProviderRuntime.js +2289 -0
  35. package/runtime/arcane/modules/AIRuntimeState.js +872 -0
  36. package/runtime/arcane/modules/ConfiguredAIChatSession.js +382 -31
  37. package/runtime/arcane/modules/DBOPFSDocumentLibrary.js +1106 -0
  38. package/runtime/arcane/modules/DocumentLexicalSearch.js +292 -0
  39. package/runtime/arcane/modules/PersistentAIChatSession.js +268 -0
  40. package/runtime/arcane/modules/StaticDocumentCatalog.js +25 -206
  41. package/schemas/arcane-lock.schema.json +10 -6
  42. package/src/cli/main.mjs +14 -2
  43. package/src/constants.mjs +1 -1
  44. package/src/dev-server.mjs +273 -26
  45. package/src/doctor.mjs +1 -3
  46. package/src/import-map.mjs +193 -84
  47. package/src/packager/core.mjs +313 -41
  48. package/src/runtime.mjs +14 -4
  49. package/src/scaffold.mjs +45 -17
  50. package/src/sdk-browser-runtime.mjs +28 -75
  51. package/src/templates/workspace-template.mjs +27 -8
  52. package/src/toolchain.mjs +13 -2
  53. package/src/workspace-runtime.mjs +1 -1
  54. package/src/workspace.mjs +178 -25
package/src/workspace.mjs CHANGED
@@ -2,6 +2,7 @@ import {createHash} from 'node:crypto';
2
2
  import {lstat,readFile,readdir,realpath} from 'node:fs/promises';
3
3
  import path from 'node:path';
4
4
  import {
5
+ normalizeRelativePath,
5
6
  validateAppConfig as validatePackagerAppConfig,
6
7
  validateRootConfig as validatePackagerRootConfig
7
8
  } from './packager/core.mjs';
@@ -19,6 +20,8 @@ import {
19
20
  const APP_ID_PATTERN=/^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$/;
20
21
  const SHA256_PATTERN=/^[a-f0-9]{64}$/;
21
22
  const LOCAL_TARBALL_PATTERN=/^file:.+\.tgz$/iu;
23
+ const NPM_PACKAGE_NAME_PATTERN=/^(?:@[a-z0-9][a-z0-9._-]*\/)?[a-z0-9][a-z0-9._-]*$/u;
24
+ const EXACT_SDK_ALIAS=`npm:${EXPECTED_SDK_NAME}@${EXPECTED_SDK_VERSION}`;
22
25
  const ROOT_CONFIG_NAME='arcane-packager.json';
23
26
  const APP_CONFIG_NAME='arcane-package.json';
24
27
 
@@ -145,10 +148,105 @@ async function assertRealDirectoryIdentity(captured,label){
145
148
  }
146
149
  }
147
150
 
151
+ function sdkPackageSourceForDependency(dependencyName){
152
+ if(typeof dependencyName!=='string'||dependencyName.length>214
153
+ ||!NPM_PACKAGE_NAME_PATTERN.test(dependencyName)){
154
+ fail(`Invalid installed SDK dependency name: ${String(dependencyName)}.`);
155
+ }
156
+ const source=`node_modules/${dependencyName}`;
157
+ try{
158
+ if(normalizeRelativePath(source,'installed SDK package source')!==source)throw new Error();
159
+ }catch{
160
+ fail(`Invalid installed SDK package source for dependency ${dependencyName}.`);
161
+ }
162
+ return source;
163
+ }
164
+
165
+ function dependencyNameForSdkPackageSource(source){
166
+ if(typeof source!=='string'||!source.startsWith('node_modules/'))return null;
167
+ const dependencyName=source.slice('node_modules/'.length);
168
+ try{
169
+ return sdkPackageSourceForDependency(dependencyName)===source?dependencyName:null;
170
+ }catch{
171
+ return null;
172
+ }
173
+ }
174
+
175
+ function supportedCanonicalSdkDeclaration(value){
176
+ return value===EXPECTED_SDK_VERSION
177
+ ||(typeof value==='string'&&LOCAL_TARBALL_PATTERN.test(value)
178
+ &&!/[\x00-\x1f\x7f]/.test(value));
179
+ }
180
+
181
+ export function resolveSdkPackageDeclaration(rootPackage,{
182
+ allowMissing=false,
183
+ packageSource
184
+ }={}){
185
+ if(!isObject(rootPackage))fail('package.json must contain a JSON object.');
186
+ const candidates=[];
187
+ for(const groupName of ['devDependencies','dependencies']){
188
+ const group=rootPackage[groupName];
189
+ if(group===undefined)continue;
190
+ if(!isObject(group))fail(`package.json ${groupName} must be a JSON object.`);
191
+ for(const [dependencyName,specifier] of Object.entries(group)){
192
+ const canonicalName=dependencyName===EXPECTED_SDK_NAME;
193
+ const aliasTarget=typeof specifier==='string'
194
+ &&(specifier===`npm:${EXPECTED_SDK_NAME}`
195
+ ||specifier.startsWith(`npm:${EXPECTED_SDK_NAME}@`));
196
+ if(!canonicalName&&!aliasTarget)continue;
197
+ const supported=canonicalName
198
+ ?supportedCanonicalSdkDeclaration(specifier)
199
+ :specifier===EXACT_SDK_ALIAS;
200
+ if(!supported){
201
+ fail(
202
+ `package.json ${groupName}.${dependencyName} must be ${EXPECTED_SDK_VERSION}, `
203
+ +`a local file: tarball under ${EXPECTED_SDK_NAME}, or the exact npm alias `
204
+ +`${EXACT_SDK_ALIAS} under a distinct dependency key.`
205
+ );
206
+ }
207
+ candidates.push(Object.freeze({
208
+ dependencyName,
209
+ dependencyGroup:groupName,
210
+ packageSource:sdkPackageSourceForDependency(dependencyName),
211
+ specifier
212
+ }));
213
+ }
214
+ }
215
+ if(candidates.length>1){
216
+ fail('package.json must declare exactly one Arcane SDK installation; remove duplicate canonical or alias declarations.');
217
+ }
218
+ if(candidates.length===0){
219
+ if(allowMissing&&packageSource===undefined)return null;
220
+ fail(
221
+ `package.json must declare exactly one ${EXPECTED_SDK_NAME} installation as `
222
+ +`${EXPECTED_SDK_VERSION}, a local file: tarball, or the exact npm alias ${EXACT_SDK_ALIAS}.`
223
+ );
224
+ }
225
+ const [declaration]=candidates;
226
+ if(packageSource!==undefined&&declaration.packageSource!==packageSource){
227
+ fail(
228
+ `${ROOT_CONFIG_NAME} SDK license source ${String(packageSource)} does not match `
229
+ +`the declared SDK installation ${declaration.packageSource}.`
230
+ );
231
+ }
232
+ return declaration;
233
+ }
234
+
235
+ function sdkManifestPaths(packageSource){
236
+ return Object.freeze({
237
+ runtimeManifest:`${packageSource}/runtime/ARCANE_RUNTIME_RELEASE.json`,
238
+ browserRuntimeManifest:`${packageSource}/browser-runtime/ARCANE_SDK_BROWSER_RELEASE.json`
239
+ });
240
+ }
241
+
148
242
  function classifyRootConfig(config){
149
243
  const validated=validatePackagerRootConfig(config,ROOT_CONFIG_NAME);
150
244
  const routes=validated.sharedPayloads['browser-runtime'];
151
245
  if(!Array.isArray(routes))fail(`${ROOT_CONFIG_NAME} must define browser-runtime routes.`);
246
+ const externalPackageSource=routes.length===2
247
+ &&dependencyNameForSdkPackageSource(routes[1]?.source)!==null
248
+ ?routes[1].source
249
+ :null;
152
250
  const external=[
153
251
  {
154
252
  source:'arcane',
@@ -156,7 +254,7 @@ function classifyRootConfig(config){
156
254
  include:['components','css','dependencies','entities','img','modules','sdk','security']
157
255
  },
158
256
  {
159
- source:'node_modules/arcane-os',
257
+ source:externalPackageSource,
160
258
  destination:'licenses/arcane-os',
161
259
  include:['LICENSE','COMMERCIAL-LICENSE.md','NOTICE']
162
260
  }
@@ -201,7 +299,12 @@ function classifyRootConfig(config){
201
299
  }else{
202
300
  fail(`${ROOT_CONFIG_NAME} browser-runtime routes must match the external SDK or integrated Arcane workspace contract.`);
203
301
  }
204
- return Object.freeze({...validated,workspaceMode,browserRuntimeLayout});
302
+ return Object.freeze({
303
+ ...validated,
304
+ workspaceMode,
305
+ browserRuntimeLayout,
306
+ ...(workspaceMode==='external'?{sdkPackageSource:externalPackageSource}:{})
307
+ });
205
308
  }
206
309
 
207
310
  async function discoverAppsInRoot(root,config){
@@ -307,7 +410,7 @@ export async function resolveWorkspace({workspaceRoot=process.cwd(),appId}={}){
307
410
  });
308
411
  }
309
412
 
310
- function validateLock(lock){
413
+ function validateLock(lock,sdkDeclaration){
311
414
  const browser=lock?.sdkBrowserRuntime;
312
415
  const browserSource=browser?.source;
313
416
  const dependencies=browserSource?.dependencies;
@@ -340,6 +443,7 @@ function validateLock(lock){
340
443
  &&actual.name===expected.name&&actual.version===expected.version
341
444
  &&actual.resolved===expected.resolved&&actual.integrity===expected.integrity;
342
445
  });
446
+ const manifests=sdkManifestPaths(sdkDeclaration.packageSource);
343
447
  if(!exactKeys(lock,['schemaVersion','sdk','runtime','sdkBrowserRuntime','protocols'])
344
448
  ||lock.schemaVersion!==1
345
449
  ||!exactKeys(lock.sdk,['name','version'])
@@ -347,11 +451,11 @@ function validateLock(lock){
347
451
  ||!exactKeys(lock.runtime,['manifest','contentSha256','upstreamCommit'])
348
452
  ||!SHA256_PATTERN.test(lock.runtime.contentSha256)
349
453
  ||!/^([a-f0-9]{40})$/.test(lock.runtime.upstreamCommit)
350
- ||lock.runtime.manifest!=='node_modules/arcane-os/runtime/ARCANE_RUNTIME_RELEASE.json'
454
+ ||lock.runtime.manifest!==manifests.runtimeManifest
351
455
  ||!exactKeys(browser,[
352
456
  'manifest','manifestSha256','contentSha256','builder','sdkVersion','source'
353
457
  ])
354
- ||browser.manifest!=='node_modules/arcane-os/browser-runtime/ARCANE_SDK_BROWSER_RELEASE.json'
458
+ ||browser.manifest!==manifests.browserRuntimeManifest
355
459
  ||browser.manifestSha256!==SDK_BROWSER_RUNTIME_MANIFEST_SHA256
356
460
  ||browser.contentSha256!==SDK_BROWSER_RUNTIME_CONTENT_SHA256
357
461
  ||browser.builder!=='arcane-sdk-browser-runtime-v1'
@@ -372,6 +476,43 @@ function validateLock(lock){
372
476
  return lock;
373
477
  }
374
478
 
479
+ async function resolveInstalledSdkInstallation(workspaceRoot,declaration){
480
+ let current=workspaceRoot;
481
+ for(const segment of declaration.packageSource.split('/')){
482
+ current=path.join(current,segment);
483
+ await assertRealDirectory(current,`Installed SDK package path ${declaration.packageSource}`);
484
+ }
485
+ const canonicalPackageRoot=await realpath(current);
486
+ if(!sameDirectoryPath(current,canonicalPackageRoot)){
487
+ fail(`Installed SDK package root must be one direct physical directory: ${current}.`);
488
+ }
489
+ const installedPackage=await readJson(
490
+ path.join(canonicalPackageRoot,'package.json'),
491
+ 'installed SDK package manifest'
492
+ );
493
+ if(installedPackage.name!==EXPECTED_SDK_NAME||installedPackage.version!==EXPECTED_SDK_VERSION){
494
+ fail(`Installed SDK package must identify exactly as ${EXPECTED_SDK_NAME}@${EXPECTED_SDK_VERSION}.`);
495
+ }
496
+ const runtimeRoot=path.join(canonicalPackageRoot,'runtime');
497
+ const browserRuntimeRoot=path.join(canonicalPackageRoot,'browser-runtime');
498
+ await Promise.all([
499
+ assertRealDirectory(runtimeRoot,'Installed SDK runtime root'),
500
+ assertRealDirectory(browserRuntimeRoot,'Installed SDK browser runtime root')
501
+ ]);
502
+ const manifests=sdkManifestPaths(declaration.packageSource);
503
+ return Object.freeze({
504
+ dependencyName:declaration.dependencyName,
505
+ packageSource:declaration.packageSource,
506
+ canonicalPackageRoot,
507
+ packageName:installedPackage.name,
508
+ packageVersion:installedPackage.version,
509
+ runtimeRoot,
510
+ browserRuntimeRoot,
511
+ runtimeManifest:manifests.runtimeManifest,
512
+ browserRuntimeManifest:manifests.browserRuntimeManifest
513
+ });
514
+ }
515
+
375
516
  function sameBrowserRuntimeSource(actual,pinned){
376
517
  const exactKeys=(value,keys)=>isObject(value)
377
518
  &&Object.keys(value).sort().join('\0')===[...keys].sort().join('\0');
@@ -585,6 +726,8 @@ export async function validateWorkspace({
585
726
  await emit(onEvent,{type:'workspace.validate.check',name,ok:true});
586
727
  };
587
728
  let lock;
729
+ let sdkDeclaration;
730
+ let sdkInstallation;
588
731
  let validatedApplication;
589
732
  const workspaceMode=resolved.config.workspaceMode;
590
733
  await add('workspace-profile',async()=>{
@@ -599,7 +742,14 @@ export async function validateWorkspace({
599
742
  });
600
743
  if(workspaceMode==='external'){
601
744
  await add('lock',async()=>{
602
- lock=validateLock(await readJson(path.join(resolved.workspaceRoot,'arcane.lock.json'),'arcane.lock.json'));
745
+ sdkDeclaration=resolveSdkPackageDeclaration(
746
+ await readJson(path.join(resolved.workspaceRoot,'package.json'),'package.json'),
747
+ {packageSource:resolved.config.sdkPackageSource}
748
+ );
749
+ lock=validateLock(
750
+ await readJson(path.join(resolved.workspaceRoot,'arcane.lock.json'),'arcane.lock.json'),
751
+ sdkDeclaration
752
+ );
603
753
  });
604
754
  }
605
755
  await add('package',async()=>{
@@ -610,37 +760,39 @@ export async function validateWorkspace({
610
760
  }
611
761
  return;
612
762
  }
613
- const configured=rootPackage?.devDependencies?.[EXPECTED_SDK_NAME]
614
- ??rootPackage?.dependencies?.[EXPECTED_SDK_NAME];
615
- const supported=configured===EXPECTED_SDK_VERSION
616
- ||(typeof configured==='string'&&LOCAL_TARBALL_PATTERN.test(configured)
617
- &&!/[\x00-\x1f\x7f]/.test(configured));
618
- if(rootPackage?.private!==true||rootPackage?.type!=='module'||!supported){
619
- fail(`package.json must be private, use modules, and declare ${EXPECTED_SDK_NAME} as ${EXPECTED_SDK_VERSION} or a local file: tarball.`);
763
+ const declaration=resolveSdkPackageDeclaration(rootPackage,{
764
+ packageSource:resolved.config.sdkPackageSource
765
+ });
766
+ if(rootPackage?.private!==true||rootPackage?.type!=='module'){
767
+ fail(
768
+ `package.json must be private, use modules, and declare one exact `
769
+ +`${EXPECTED_SDK_NAME} installation.`
770
+ );
771
+ }
772
+ if(declaration.dependencyName!==sdkDeclaration.dependencyName
773
+ ||declaration.dependencyGroup!==sdkDeclaration.dependencyGroup
774
+ ||declaration.packageSource!==sdkDeclaration.packageSource
775
+ ||declaration.specifier!==sdkDeclaration.specifier){
776
+ fail('The declared SDK installation changed after workspace profile inspection.',
777
+ 'ARCANE_INTEGRITY_FAILED');
620
778
  }
621
779
  });
622
780
  if(workspaceMode==='external'){
623
781
  await add('installed-runtime',async()=>{
624
- const installedPackage=await readJson(
625
- path.join(resolved.workspaceRoot,'node_modules','arcane-os','package.json'),
626
- 'installed SDK package manifest'
782
+ sdkInstallation=await resolveInstalledSdkInstallation(
783
+ resolved.workspaceRoot,
784
+ sdkDeclaration
627
785
  );
628
- if(installedPackage.name!==EXPECTED_SDK_NAME||installedPackage.version!==EXPECTED_SDK_VERSION){
629
- fail(`Installed SDK package must identify exactly as ${EXPECTED_SDK_NAME}@${EXPECTED_SDK_VERSION}.`);
630
- }
631
786
  const installed=await readJson(
632
- path.join(resolved.workspaceRoot,'node_modules','arcane-os','runtime','ARCANE_RUNTIME_RELEASE.json'),
787
+ path.join(sdkInstallation.runtimeRoot,'ARCANE_RUNTIME_RELEASE.json'),
633
788
  'installed SDK runtime manifest'
634
789
  );
635
790
  if(installed.contentSha256!==lock.runtime.contentSha256
636
- ||installed.source?.commit!==lock.runtime.upstreamCommit){
791
+ ||installed.source?.legacyProjection?.commit!==lock.runtime.upstreamCommit){
637
792
  fail('Installed SDK runtime does not match arcane.lock.json.');
638
793
  }
639
794
  const browserManifestPath=path.join(
640
- resolved.workspaceRoot,
641
- 'node_modules',
642
- 'arcane-os',
643
- 'browser-runtime',
795
+ sdkInstallation.browserRuntimeRoot,
644
796
  'ARCANE_SDK_BROWSER_RELEASE.json'
645
797
  );
646
798
  const browserBytes=await readFile(browserManifestPath);
@@ -697,6 +849,7 @@ export async function validateWorkspace({
697
849
  config:resolved.config,
698
850
  app:validatedApplication.app,
699
851
  lock,
852
+ ...(sdkInstallation?{sdkInstallation}:{}),
700
853
  checks:Object.freeze(checks)
701
854
  });
702
855
  await emit(onEvent,{type:'workspace.validate.completed',workspaceRoot:resolved.workspaceRoot,appId:resolved.appId,checks});