arcane-os 0.2.0 → 0.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +35 -0
- package/README.md +8 -8
- package/browser-runtime/ARCANE_SDK_BROWSER_RELEASE.json +5 -5
- package/browser-runtime/ai/browser-speech-providers.mjs +331 -25
- package/docs/architecture.md +2 -2
- package/docs/reference/README.md +79 -13
- package/docs/reference/ai/browser-speech.md +336 -0
- package/docs/reference/ai/browser-wasm.md +207 -82
- package/docs/reference/availability-and-normalization.md +30 -4
- package/docs/reference/behavioral-testing.md +4 -1
- package/docs/reference/cli.md +29 -10
- package/docs/reference/core/arcane-ai-contracts.md +43 -9
- package/docs/reference/inventory/package-api.json +110 -14
- package/docs/reference/inventory/runtime-components.json +19 -6
- package/docs/reference/inventory/runtime-modules.json +113 -9
- package/docs/reference/protocols.md +260 -38
- package/docs/reference/runtime-components.md +108 -15
- package/docs/reference/runtime-modules.md +422 -8
- package/docs/reference/sdk-api.md +626 -85
- package/package.json +1 -1
- package/runtime/ARCANE_RUNTIME_RELEASE.json +19 -19
- package/runtime/arcane/components/chat.html +288 -52
- package/runtime/arcane/components/speech.html +339 -15
- package/runtime/arcane/modules/AI.js +1245 -136
- package/runtime/arcane/modules/AIProviderRuntime.js +299 -30
- package/runtime/arcane/modules/AIRuntimeState.js +23 -4
- package/runtime/arcane/modules/ConfiguredAIChatSession.js +93 -8
- package/runtime/arcane/modules/DBOPFSDocumentLibrary.js +448 -24
- package/runtime/arcane/modules/LocalAIReadinessController.js +1 -1
- package/schemas/arcane-lock.schema.json +6 -4
- package/src/dev-server.mjs +29 -13
- package/src/doctor.mjs +1 -3
- package/src/import-map.mjs +134 -83
- package/src/packager/core.mjs +311 -39
- package/src/scaffold.mjs +45 -17
- package/src/templates/workspace-template.mjs +23 -4
- package/src/toolchain.mjs +10 -2
- package/src/workspace.mjs +177 -24
|
@@ -4,6 +4,9 @@ import {
|
|
|
4
4
|
SDK_BROWSER_RUNTIME_MANIFEST_SHA256
|
|
5
5
|
} from '../sdk-browser-runtime.mjs';
|
|
6
6
|
|
|
7
|
+
const NPM_PACKAGE_NAME_PATTERN=/^(?:@[a-z0-9][a-z0-9._-]*\/)?[a-z0-9][a-z0-9._-]*$/u;
|
|
8
|
+
const LOCAL_TARBALL_PATTERN=/^file:.+\.tgz$/iu;
|
|
9
|
+
|
|
7
10
|
function json(value){
|
|
8
11
|
return `${JSON.stringify(value,null,2)}\n`;
|
|
9
12
|
}
|
|
@@ -28,12 +31,28 @@ export function workspaceTemplate({
|
|
|
28
31
|
displayName,
|
|
29
32
|
runtimeRelease,
|
|
30
33
|
sdkBrowserRuntimeRelease,
|
|
34
|
+
sdkDependencyName=SDK_NAME,
|
|
35
|
+
sdkDependencySpecifier=SDK_VERSION,
|
|
36
|
+
sdkPackageSource=`node_modules/${sdkDependencyName}`,
|
|
31
37
|
appOnly=false,
|
|
32
38
|
namedImports=true,
|
|
33
39
|
minimumCoreVersion='0.8.12',
|
|
34
40
|
target='browser',
|
|
35
41
|
appIcon
|
|
36
42
|
}){
|
|
43
|
+
const canonicalSpecifier=sdkDependencyName===SDK_NAME
|
|
44
|
+
&&(sdkDependencySpecifier===SDK_VERSION
|
|
45
|
+
||(typeof sdkDependencySpecifier==='string'
|
|
46
|
+
&&LOCAL_TARBALL_PATTERN.test(sdkDependencySpecifier)
|
|
47
|
+
&&!/[\x00-\x1f\x7f]/.test(sdkDependencySpecifier)));
|
|
48
|
+
const aliasSpecifier=sdkDependencyName!==SDK_NAME
|
|
49
|
+
&&sdkDependencySpecifier===`npm:${SDK_NAME}@${SDK_VERSION}`;
|
|
50
|
+
if(typeof sdkDependencyName!=='string'||sdkDependencyName.length>214
|
|
51
|
+
||!NPM_PACKAGE_NAME_PATTERN.test(sdkDependencyName)
|
|
52
|
+
||sdkPackageSource!==`node_modules/${sdkDependencyName}`
|
|
53
|
+
||(!canonicalSpecifier&&!aliasSpecifier)){
|
|
54
|
+
throw new Error('Invalid scaffold SDK installation authority.');
|
|
55
|
+
}
|
|
37
56
|
const supportedTargets=['browser','portable','windows-x64','linux-x64','linux-arm64','android-arm64'];
|
|
38
57
|
if(!supportedTargets.includes(target)){
|
|
39
58
|
throw new Error(`Unsupported scaffold target: ${String(target)}.`);
|
|
@@ -222,7 +241,7 @@ Every browser release also carries Arcane OS licensing material under \`licenses
|
|
|
222
241
|
}:{})
|
|
223
242
|
},
|
|
224
243
|
devDependencies:{
|
|
225
|
-
[
|
|
244
|
+
[sdkDependencyName]:sdkDependencySpecifier
|
|
226
245
|
},
|
|
227
246
|
engines:{node:'>=22.23.2'}
|
|
228
247
|
}));
|
|
@@ -239,7 +258,7 @@ Every browser release also carries Arcane OS licensing material under \`licenses
|
|
|
239
258
|
exclude:[]
|
|
240
259
|
},
|
|
241
260
|
{
|
|
242
|
-
source:
|
|
261
|
+
source:sdkPackageSource,
|
|
243
262
|
destination:'licenses/arcane-os',
|
|
244
263
|
include:['LICENSE','COMMERCIAL-LICENSE.md','NOTICE'],
|
|
245
264
|
exclude:[]
|
|
@@ -251,12 +270,12 @@ Every browser release also carries Arcane OS licensing material under \`licenses
|
|
|
251
270
|
schemaVersion:1,
|
|
252
271
|
sdk:{name:SDK_NAME,version:SDK_VERSION},
|
|
253
272
|
runtime:{
|
|
254
|
-
manifest
|
|
273
|
+
manifest:`${sdkPackageSource}/runtime/ARCANE_RUNTIME_RELEASE.json`,
|
|
255
274
|
contentSha256:runtimeContentSha256,
|
|
256
275
|
upstreamCommit
|
|
257
276
|
},
|
|
258
277
|
sdkBrowserRuntime:{
|
|
259
|
-
manifest
|
|
278
|
+
manifest:`${sdkPackageSource}/browser-runtime/ARCANE_SDK_BROWSER_RELEASE.json`,
|
|
260
279
|
manifestSha256:browserManifestSha256,
|
|
261
280
|
contentSha256:browserContentSha256,
|
|
262
281
|
builder:sdkBrowserRuntimeRelease?.builder,
|
package/src/toolchain.mjs
CHANGED
|
@@ -380,11 +380,19 @@ async function preparedWorkspace(options){
|
|
|
380
380
|
onEvent:options.onEvent
|
|
381
381
|
});
|
|
382
382
|
const external=workspace.workspaceMode==='external';
|
|
383
|
+
if(external&&(!workspace.sdkInstallation
|
|
384
|
+
||typeof workspace.sdkInstallation.runtimeRoot!=='string'
|
|
385
|
+
||typeof workspace.sdkInstallation.browserRuntimeRoot!=='string')){
|
|
386
|
+
throw new ArcaneError(
|
|
387
|
+
ERROR_CODES.workspaceInvalid,
|
|
388
|
+
'Validated external workspace is missing its bound SDK installation authority.'
|
|
389
|
+
);
|
|
390
|
+
}
|
|
383
391
|
const runtimeRoot=external
|
|
384
|
-
?
|
|
392
|
+
?workspace.sdkInstallation.runtimeRoot
|
|
385
393
|
:workspace.workspaceRoot;
|
|
386
394
|
const browserRuntimeRoot=external
|
|
387
|
-
?
|
|
395
|
+
?workspace.sdkInstallation.browserRuntimeRoot
|
|
388
396
|
:null;
|
|
389
397
|
const runtimeReceipt=external&&!options.deferRuntimeVerification
|
|
390
398
|
?await verifyRuntime({
|
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:
|
|
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({
|
|
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!==
|
|
454
|
+
||lock.runtime.manifest!==manifests.runtimeManifest
|
|
351
455
|
||!exactKeys(browser,[
|
|
352
456
|
'manifest','manifestSha256','contentSha256','builder','sdkVersion','source'
|
|
353
457
|
])
|
|
354
|
-
||browser.manifest!==
|
|
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
|
-
|
|
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,26 +760,31 @@ export async function validateWorkspace({
|
|
|
610
760
|
}
|
|
611
761
|
return;
|
|
612
762
|
}
|
|
613
|
-
const
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
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
|
-
|
|
625
|
-
|
|
626
|
-
|
|
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(
|
|
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
|
|
@@ -637,10 +792,7 @@ export async function validateWorkspace({
|
|
|
637
792
|
fail('Installed SDK runtime does not match arcane.lock.json.');
|
|
638
793
|
}
|
|
639
794
|
const browserManifestPath=path.join(
|
|
640
|
-
|
|
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});
|