arcane-os 0.1.0-dev.5

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 (248) hide show
  1. package/CHANGELOG.md +154 -0
  2. package/COMMERCIAL-LICENSE.md +13 -0
  3. package/LICENSE +661 -0
  4. package/NOTICE +70 -0
  5. package/README.md +448 -0
  6. package/bin/arcane-test.mjs +741 -0
  7. package/bin/arcane.mjs +5 -0
  8. package/docs/architecture.md +234 -0
  9. package/docs/compatibility.md +36 -0
  10. package/docs/event-manager.md +166 -0
  11. package/docs/platform-targets.md +108 -0
  12. package/docs/publishing.md +203 -0
  13. package/docs/reference/README.md +117 -0
  14. package/docs/reference/arcane-ollama.md +288 -0
  15. package/docs/reference/availability-and-normalization.md +123 -0
  16. package/docs/reference/behavioral-testing.md +86 -0
  17. package/docs/reference/cli.md +569 -0
  18. package/docs/reference/core/README.md +62 -0
  19. package/docs/reference/core/arcane-ai-contracts.md +873 -0
  20. package/docs/reference/core/arcane-api.md +601 -0
  21. package/docs/reference/core/arcane-entities.md +65 -0
  22. package/docs/reference/core/arcane-events.md +134 -0
  23. package/docs/reference/core/ollama-module.md +181 -0
  24. package/docs/reference/core/reference/arcane-api/ai-and-ollama.md +1909 -0
  25. package/docs/reference/core/reference/arcane-api/applications-terminal-capabilities.md +1057 -0
  26. package/docs/reference/core/reference/arcane-api/core-and-events.md +320 -0
  27. package/docs/reference/core/reference/arcane-api/filesystem-storage-preferences-appearance.md +610 -0
  28. package/docs/reference/core/reference/arcane-api/namespaces.md +1157 -0
  29. package/docs/reference/core/reference/arcane-api/platform-installation-users-system.md +1423 -0
  30. package/docs/reference/core/reference/arcane-api/session-provisioning-diagnostics-development.md +315 -0
  31. package/docs/reference/event-manager.md +957 -0
  32. package/docs/reference/inventory/package-api.json +2632 -0
  33. package/docs/reference/inventory/runtime-components.json +934 -0
  34. package/docs/reference/inventory/runtime-entities.json +26 -0
  35. package/docs/reference/inventory/runtime-modules.json +1249 -0
  36. package/docs/reference/protocols.md +242 -0
  37. package/docs/reference/runtime-components.md +1098 -0
  38. package/docs/reference/runtime-entities.md +303 -0
  39. package/docs/reference/runtime-modules.md +2010 -0
  40. package/docs/reference/sdk-api.md +4901 -0
  41. package/docs/roadmap.md +79 -0
  42. package/docs/work-amplification.md +124 -0
  43. package/node_modules/event-pubsub/CHANGELOG.md +55 -0
  44. package/node_modules/event-pubsub/MIGRATION.md +70 -0
  45. package/node_modules/event-pubsub/README.md +363 -0
  46. package/node_modules/event-pubsub/SECURITY.md +37 -0
  47. package/node_modules/event-pubsub/index.js +141 -0
  48. package/node_modules/event-pubsub/licence +21 -0
  49. package/node_modules/event-pubsub/package.json +59 -0
  50. package/node_modules/strong-type/README.md +408 -0
  51. package/node_modules/strong-type/assets/strong-type-header.png +0 -0
  52. package/node_modules/strong-type/index.js +1151 -0
  53. package/node_modules/strong-type/licence +21 -0
  54. package/node_modules/strong-type/node.js +125 -0
  55. package/node_modules/strong-type/package.json +61 -0
  56. package/package.json +95 -0
  57. package/runtime/ARCANE_RUNTIME_RELEASE.json +791 -0
  58. package/runtime/arcane/components/app-bar.html +468 -0
  59. package/runtime/arcane/components/assistant-panel.html +715 -0
  60. package/runtime/arcane/components/calculator.html +8 -0
  61. package/runtime/arcane/components/chart.html +655 -0
  62. package/runtime/arcane/components/chat.html +1225 -0
  63. package/runtime/arcane/components/conversation-view.html +13 -0
  64. package/runtime/arcane/components/dashboard-config.html +341 -0
  65. package/runtime/arcane/components/data-maintenance.html +112 -0
  66. package/runtime/arcane/components/data-view.html +92 -0
  67. package/runtime/arcane/components/directory-picker.html +197 -0
  68. package/runtime/arcane/components/document-inspector.html +252 -0
  69. package/runtime/arcane/components/file-drop.html +264 -0
  70. package/runtime/arcane/components/file-inspector.html +293 -0
  71. package/runtime/arcane/components/file-manager.html +1715 -0
  72. package/runtime/arcane/components/header.html +142 -0
  73. package/runtime/arcane/components/integration-settings.html +14 -0
  74. package/runtime/arcane/components/local-ai-status.html +360 -0
  75. package/runtime/arcane/components/markdown-document.html +1048 -0
  76. package/runtime/arcane/components/markdown-editor.html +360 -0
  77. package/runtime/arcane/components/media-embed.html +8 -0
  78. package/runtime/arcane/components/modal.html +402 -0
  79. package/runtime/arcane/components/output-panel.html +259 -0
  80. package/runtime/arcane/components/preferences-form.html +135 -0
  81. package/runtime/arcane/components/record-timeline.html +105 -0
  82. package/runtime/arcane/components/relationship-board.html +116 -0
  83. package/runtime/arcane/components/screen-capture.html +8 -0
  84. package/runtime/arcane/components/source-code-viewer.html +441 -0
  85. package/runtime/arcane/components/source-explanation.html +124 -0
  86. package/runtime/arcane/components/speech.html +365 -0
  87. package/runtime/arcane/components/summary-strip.html +177 -0
  88. package/runtime/arcane/components/table.html +77 -0
  89. package/runtime/arcane/components/task-progress.html +282 -0
  90. package/runtime/arcane/components/terminal-workspace.html +65 -0
  91. package/runtime/arcane/components/theme-editor.html +41 -0
  92. package/runtime/arcane/components/theme-switcher.html +46 -0
  93. package/runtime/arcane/components/unified-inbox.html +20 -0
  94. package/runtime/arcane/components/voice-transcription.html +476 -0
  95. package/runtime/arcane/components/weather-widget.html +8 -0
  96. package/runtime/arcane/components/web-navigator.html +239 -0
  97. package/runtime/arcane/css/communications.css +1 -0
  98. package/runtime/arcane/css/dashboard-config.css +45 -0
  99. package/runtime/arcane/css/document-site.css +981 -0
  100. package/runtime/arcane/css/layout.css +438 -0
  101. package/runtime/arcane/css/primitives.css +321 -0
  102. package/runtime/arcane/css/theme.css +112 -0
  103. package/runtime/arcane/css/utility-workspace.css +1 -0
  104. package/runtime/arcane/entities/ApiModelRecord.js +20 -0
  105. package/runtime/arcane/entities/Calculation.js +13 -0
  106. package/runtime/arcane/entities/Chat.js +581 -0
  107. package/runtime/arcane/entities/CommunicationMessage.js +29 -0
  108. package/runtime/arcane/entities/CommunicationThread.js +21 -0
  109. package/runtime/arcane/entities/Document.js +10 -0
  110. package/runtime/arcane/entities/File.js +143 -0
  111. package/runtime/arcane/entities/Image.js +135 -0
  112. package/runtime/arcane/entities/IntentEnvelope.js +834 -0
  113. package/runtime/arcane/entities/Preference.js +83 -0
  114. package/runtime/arcane/entities/TWiNPolicyDecision.js +1092 -0
  115. package/runtime/arcane/entities/TerminalSession.js +46 -0
  116. package/runtime/arcane/entities/Theme.js +107 -0
  117. package/runtime/arcane/entities/User.js +1046 -0
  118. package/runtime/arcane/entities/Weather.js +23 -0
  119. package/runtime/arcane/img/arcane-os-everywhere.png +0 -0
  120. package/runtime/arcane/img/arrow-left.png +0 -0
  121. package/runtime/arcane/img/arrow-right.png +0 -0
  122. package/runtime/arcane/img/doc.svg +5 -0
  123. package/runtime/arcane/img/folder.svg +4 -0
  124. package/runtime/arcane/img/image.svg +5 -0
  125. package/runtime/arcane/img/refresh.png +0 -0
  126. package/runtime/arcane/img/send.svg +9 -0
  127. package/runtime/arcane/img/trash.svg +5 -0
  128. package/runtime/arcane/img/upload.svg +5 -0
  129. package/runtime/arcane/modules/AI.js +2048 -0
  130. package/runtime/arcane/modules/AIPreferenceRuntime.js +32 -0
  131. package/runtime/arcane/modules/AIPreferenceTuple.js +92 -0
  132. package/runtime/arcane/modules/AIResponseLength.js +42 -0
  133. package/runtime/arcane/modules/AIResponseURLPolicy.js +626 -0
  134. package/runtime/arcane/modules/AnsiText.js +53 -0
  135. package/runtime/arcane/modules/ApiModelDatabase.js +25 -0
  136. package/runtime/arcane/modules/AppDataScope.js +245 -0
  137. package/runtime/arcane/modules/AppearancePreferences.js +28 -0
  138. package/runtime/arcane/modules/ArcaneCommunicationBridge.js +22 -0
  139. package/runtime/arcane/modules/ArcaneNavigationPolicy.js +135 -0
  140. package/runtime/arcane/modules/ArcaneNetworkPolicy.js +255 -0
  141. package/runtime/arcane/modules/AsyncBoundary.js +161 -0
  142. package/runtime/arcane/modules/BrowserTestSuite.js +326 -0
  143. package/runtime/arcane/modules/CalculatorEngine.js +20 -0
  144. package/runtime/arcane/modules/CaseEvidenceIndexer.js +134 -0
  145. package/runtime/arcane/modules/ChartLibrary.js +35 -0
  146. package/runtime/arcane/modules/ChatRecords.js +13 -0
  147. package/runtime/arcane/modules/CommunicationAppController.js +43 -0
  148. package/runtime/arcane/modules/CommunicationHub.js +16 -0
  149. package/runtime/arcane/modules/CommunicationPreferences.js +13 -0
  150. package/runtime/arcane/modules/CommunicationProviderRegistry.js +16 -0
  151. package/runtime/arcane/modules/ComponentContracts.js +586 -0
  152. package/runtime/arcane/modules/ConfiguredAIChatSession.js +288 -0
  153. package/runtime/arcane/modules/ConversationActionItems.js +488 -0
  154. package/runtime/arcane/modules/ConversationClosingReport.js +274 -0
  155. package/runtime/arcane/modules/ConversationTimebox.js +527 -0
  156. package/runtime/arcane/modules/CoreLocalModelCatalog.js +255 -0
  157. package/runtime/arcane/modules/DBLS.js +171 -0
  158. package/runtime/arcane/modules/DBOPFS.js +1154 -0
  159. package/runtime/arcane/modules/DBOPFSWorker.js +116 -0
  160. package/runtime/arcane/modules/DataMaintenance.js +91 -0
  161. package/runtime/arcane/modules/DevelopmentWorkspace.js +74 -0
  162. package/runtime/arcane/modules/DirectoryPicker.js +109 -0
  163. package/runtime/arcane/modules/DocumentNavigation.js +223 -0
  164. package/runtime/arcane/modules/Errors.js +1025 -0
  165. package/runtime/arcane/modules/GifEncoder.js +29 -0
  166. package/runtime/arcane/modules/HTMLImport.js +114 -0
  167. package/runtime/arcane/modules/InMemoryCommunicationProvider.js +14 -0
  168. package/runtime/arcane/modules/IsolatedModelQuestionRunner.js +275 -0
  169. package/runtime/arcane/modules/LocalAIReadiness.js +870 -0
  170. package/runtime/arcane/modules/LocalAIReadinessController.js +156 -0
  171. package/runtime/arcane/modules/MD.js +111 -0
  172. package/runtime/arcane/modules/Mail.js +352 -0
  173. package/runtime/arcane/modules/MailTransport.mjs +180 -0
  174. package/runtime/arcane/modules/Marked.min.js +71 -0
  175. package/runtime/arcane/modules/MemoryRecords.js +44 -0
  176. package/runtime/arcane/modules/MessageAdvisory.js +38 -0
  177. package/runtime/arcane/modules/ModelDefinition.js +189 -0
  178. package/runtime/arcane/modules/Ollama.js +74 -0
  179. package/runtime/arcane/modules/OllamaModelIdentifier.js +21 -0
  180. package/runtime/arcane/modules/OllamaSettings.js +24 -0
  181. package/runtime/arcane/modules/OpenMeteoWeatherProvider.js +14 -0
  182. package/runtime/arcane/modules/PreferenceStore.js +109 -0
  183. package/runtime/arcane/modules/QRCode.min.js +1 -0
  184. package/runtime/arcane/modules/Questionnaire.js +61 -0
  185. package/runtime/arcane/modules/RecordLinkIndex.js +24 -0
  186. package/runtime/arcane/modules/RecordPassageIndex.js +222 -0
  187. package/runtime/arcane/modules/RecordReviewStore.js +94 -0
  188. package/runtime/arcane/modules/RevocableProjectionLedger.js +1623 -0
  189. package/runtime/arcane/modules/RiskSignalAnalyzer.js +37 -0
  190. package/runtime/arcane/modules/ScamRiskPolicy.js +62 -0
  191. package/runtime/arcane/modules/ScopedOPFSCache.js +183 -0
  192. package/runtime/arcane/modules/ScreenCapture.js +20 -0
  193. package/runtime/arcane/modules/SpeechPlayback.js +581 -0
  194. package/runtime/arcane/modules/StaticDocumentCatalog.js +1248 -0
  195. package/runtime/arcane/modules/SystemAppearance.js +20 -0
  196. package/runtime/arcane/modules/SystemPlatformPresentation.js +51 -0
  197. package/runtime/arcane/modules/SystemToolRegistry.js +28 -0
  198. package/runtime/arcane/modules/TerminalClient.js +52 -0
  199. package/runtime/arcane/modules/TerminalCommandRegistry.js +50 -0
  200. package/runtime/arcane/modules/ThemeBootstrap.js +26 -0
  201. package/runtime/arcane/modules/ThemeManager.js +131 -0
  202. package/runtime/arcane/modules/TimeGuard.js +149 -0
  203. package/runtime/arcane/modules/ToolCallRouter.js +83 -0
  204. package/runtime/arcane/modules/WaitForComponent.js +102 -0
  205. package/runtime/arcane/modules/YouTubeMedia.js +16 -0
  206. package/runtime/arcane/modules/uPlot.LICENSE.txt +21 -0
  207. package/runtime/arcane/modules/uPlot.iife.min.js +2 -0
  208. package/runtime/arcane/modules/uPlot.min.css +1 -0
  209. package/runtime/arcane/security/arcane-network-policy.json +6 -0
  210. package/runtime/strong-type/index.js +352 -0
  211. package/runtime/strong-type/licence +21 -0
  212. package/runtime/strong-type/package.json +45 -0
  213. package/schemas/arcane-app-bundle.schema.json +125 -0
  214. package/schemas/arcane-app.schema.json +329 -0
  215. package/schemas/arcane-lock.schema.json +86 -0
  216. package/schemas/arcane-package.schema.json +224 -0
  217. package/schemas/cli-event.schema.json +122 -0
  218. package/schemas/event-stack.schema.json +152 -0
  219. package/schemas/native-build-plan.schema.json +176 -0
  220. package/schemas/target-adapter.schema.json +117 -0
  221. package/src/app-descriptor.mjs +500 -0
  222. package/src/cli/main.mjs +561 -0
  223. package/src/constants.mjs +31 -0
  224. package/src/dev-server.mjs +718 -0
  225. package/src/doctor.mjs +315 -0
  226. package/src/dom-event-instrumentation.mjs +594 -0
  227. package/src/errors.mjs +75 -0
  228. package/src/event-manager.mjs +1342 -0
  229. package/src/event-queue.mjs +138 -0
  230. package/src/events.mjs +219 -0
  231. package/src/index.mjs +177 -0
  232. package/src/integrated-provider-loader.mjs +432 -0
  233. package/src/native-plan.mjs +698 -0
  234. package/src/native-provider-loader.mjs +1126 -0
  235. package/src/packager/core.mjs +2691 -0
  236. package/src/process.mjs +353 -0
  237. package/src/release-bundle.mjs +2523 -0
  238. package/src/repository.mjs +90 -0
  239. package/src/runtime.mjs +452 -0
  240. package/src/scaffold.mjs +380 -0
  241. package/src/targets/index.mjs +436 -0
  242. package/src/templates/assets/app-icon.png +0 -0
  243. package/src/templates/workspace-template.mjs +388 -0
  244. package/src/testing-loader.mjs +9 -0
  245. package/src/testing.mjs +427 -0
  246. package/src/toolchain.mjs +1335 -0
  247. package/src/update-check.mjs +307 -0
  248. package/src/workspace.mjs +449 -0
@@ -0,0 +1,698 @@
1
+ import {randomBytes} from 'node:crypto';
2
+ import {lstat,realpath} from 'node:fs/promises';
3
+ import path from 'node:path';
4
+ import {
5
+ appDescriptorSha256,
6
+ projectNativeDescriptor,
7
+ projectPackageManifest,
8
+ validateAppDescriptor
9
+ } from './app-descriptor.mjs';
10
+ import {
11
+ authenticateAppReleaseAuthority,
12
+ parseSemver,
13
+ readVerifiedAppReleaseFile
14
+ } from './packager/core.mjs';
15
+ import {ArcaneError,ERROR_CODES,throwIfAborted} from './errors.mjs';
16
+
17
+ export const NATIVE_BUILD_PLAN_PROTOCOL='arcane-native-build-plan/1';
18
+ export const NATIVE_BUILDER_PROTOCOL='arcane-native-builder/1';
19
+
20
+ const NATIVE_TARGETS=Object.freeze({
21
+ portable:Object.freeze({
22
+ platforms:new Set(['windows','linux']),
23
+ architectures:new Set(['x64','arm64']),
24
+ format:'portable',
25
+ signingMode:'unsigned-local-test',
26
+ signingProfileId:null
27
+ }),
28
+ 'windows-x64':Object.freeze({
29
+ platforms:new Set(['windows']),architectures:new Set(['x64']),format:'exe',
30
+ signingMode:'unsigned-local-test',signingProfileId:null
31
+ }),
32
+ 'linux-x64':Object.freeze({
33
+ platforms:new Set(['linux']),architectures:new Set(['x64']),format:'deb',
34
+ signingMode:'unsigned-local-test',signingProfileId:null
35
+ }),
36
+ 'linux-arm64':Object.freeze({
37
+ platforms:new Set(['linux']),architectures:new Set(['arm64']),format:'deb',
38
+ signingMode:'unsigned-local-test',signingProfileId:null
39
+ }),
40
+ 'android-arm64':Object.freeze({
41
+ platforms:new Set(['android']),architectures:new Set(['arm64']),format:'apk',
42
+ signingMode:'development',signingProfileId:'arcane-android-development-v1'
43
+ })
44
+ });
45
+ const SHA256_PATTERN=/^[a-f0-9]{64}$/u;
46
+ const issuedPlans=new WeakMap();
47
+ const attemptedPlans=new WeakSet();
48
+
49
+ function fail(message,code=ERROR_CODES.policyDenied,details){
50
+ throw new ArcaneError(code,message,{details});
51
+ }
52
+
53
+ function isObject(value){
54
+ return value!==null&&typeof value==='object'&&!Array.isArray(value);
55
+ }
56
+
57
+ function assertOnlyKeys(value,allowed,label){
58
+ if(!isObject(value))fail(`${label} must be an object.`);
59
+ for(const key of Object.keys(value)){
60
+ if(!allowed.has(key))fail(`${label} contains an unsupported key: ${key}.`);
61
+ }
62
+ }
63
+
64
+ function immutable(value){
65
+ if(Array.isArray(value))return Object.freeze(value.map(immutable));
66
+ if(isObject(value))return Object.freeze(Object.fromEntries(
67
+ Object.entries(value).map(([key,item])=>[key,immutable(item)])
68
+ ));
69
+ return value;
70
+ }
71
+
72
+ function compareSemver(leftValue,rightValue){
73
+ const left=parseSemver(leftValue);
74
+ const right=parseSemver(rightValue);
75
+ for(const key of ['major','minor','patch']){
76
+ if(left[key]!==right[key])return left[key]<right[key]?-1:1;
77
+ }
78
+ if(!left.prerelease.length&&!right.prerelease.length)return 0;
79
+ if(!left.prerelease.length)return 1;
80
+ if(!right.prerelease.length)return -1;
81
+ const length=Math.max(left.prerelease.length,right.prerelease.length);
82
+ for(let index=0;index<length;index+=1){
83
+ const leftPart=left.prerelease[index];
84
+ const rightPart=right.prerelease[index];
85
+ if(leftPart===undefined)return -1;
86
+ if(rightPart===undefined)return 1;
87
+ if(leftPart===rightPart)continue;
88
+ const leftNumeric=/^\d+$/u.test(leftPart);
89
+ const rightNumeric=/^\d+$/u.test(rightPart);
90
+ if(leftNumeric&&rightNumeric){
91
+ if(leftPart.length!==rightPart.length)return leftPart.length<rightPart.length?-1:1;
92
+ return leftPart<rightPart?-1:1;
93
+ }
94
+ if(leftNumeric!==rightNumeric)return leftNumeric?-1:1;
95
+ return leftPart<rightPart?-1:1;
96
+ }
97
+ return 0;
98
+ }
99
+
100
+ function canonicalContractList(value,label,{maximum=4096}={}){
101
+ if(!Array.isArray(value)||value.length>maximum||value.some(item=>typeof item!=='string'||!item)){
102
+ fail(`${label} must be a bounded, unique, sorted string array.`,ERROR_CODES.integrityFailed);
103
+ }
104
+ const sorted=[...value].sort((left,right)=>left.localeCompare(right,'en'));
105
+ if(new Set(value).size!==value.length||value.some((item,index)=>item!==sorted[index])){
106
+ fail(`${label} must be a bounded, unique, sorted string array.`,ERROR_CODES.integrityFailed);
107
+ }
108
+ return value;
109
+ }
110
+
111
+ function assertRequirementsAvailable(required,available,label){
112
+ if(!Array.isArray(available)){
113
+ fail(`The native toolchain receipt does not bind supported ${label}.`,ERROR_CODES.integrityFailed);
114
+ }
115
+ const missing=required.filter(value=>!available.includes(value));
116
+ if(missing.length){
117
+ fail(
118
+ `The native toolchain does not provide required ${label}: ${missing.join(', ')}.`,
119
+ ERROR_CODES.targetUnavailable
120
+ );
121
+ }
122
+ }
123
+
124
+ export function assertNativeToolchainCompatibility({
125
+ appDescriptor,
126
+ toolchainReceipt,
127
+ minimumCoreVersion
128
+ }={}){
129
+ const descriptor=validateAppDescriptor(appDescriptor,{appId:appDescriptor?.id});
130
+ const requirements=descriptor.requirements;
131
+ if(!isObject(toolchainReceipt)||typeof toolchainReceipt.version!=='string'){
132
+ fail('Native compatibility inputs are incomplete.',ERROR_CODES.integrityFailed);
133
+ }
134
+ if(toolchainReceipt.protocolVersion!==requirements.arcaneProtocol){
135
+ fail(
136
+ `The app requires ${requirements.arcaneProtocol}, but the native toolchain provides ${String(toolchainReceipt.protocolVersion??'unknown')}.`,
137
+ ERROR_CODES.targetUnavailable
138
+ );
139
+ }
140
+ const requiredVersions=[requirements.minimumCoreVersion];
141
+ if(minimumCoreVersion!==undefined)requiredVersions.push(minimumCoreVersion);
142
+ for(const requiredVersion of requiredVersions){
143
+ try{
144
+ if(typeof requiredVersion!=='string'||compareSemver(toolchainReceipt.version,requiredVersion)<0){
145
+ fail(
146
+ `Arcane Core ${toolchainReceipt.version} does not meet the required minimum ${String(requiredVersion??'unknown')}.`,
147
+ ERROR_CODES.targetUnavailable
148
+ );
149
+ }
150
+ }catch(error){
151
+ if(error instanceof ArcaneError)throw error;
152
+ fail('Native compatibility contains an invalid Arcane version.',ERROR_CODES.integrityFailed);
153
+ }
154
+ }
155
+ assertRequirementsAvailable(requirements.features,toolchainReceipt.features,'features');
156
+ assertRequirementsAvailable(
157
+ descriptor.permissions.capabilities,
158
+ toolchainReceipt.supportedCapabilities,
159
+ 'capabilities'
160
+ );
161
+ assertRequirementsAvailable(
162
+ descriptor.permissions.methods,
163
+ toolchainReceipt.supportedMethods,
164
+ 'methods'
165
+ );
166
+ return descriptor;
167
+ }
168
+
169
+ function highestCoreMinimum(versions){
170
+ let highest=null;
171
+ for(const version of versions){
172
+ if(version===undefined)continue;
173
+ try{
174
+ parseSemver(version);
175
+ }catch(error){
176
+ fail('Native compatibility contains an invalid minimum Core version.',ERROR_CODES.integrityFailed);
177
+ }
178
+ if(highest===null||compareSemver(version,highest)>0)highest=version;
179
+ }
180
+ if(highest===null)fail('Native compatibility has no minimum Core version.',ERROR_CODES.integrityFailed);
181
+ return highest;
182
+ }
183
+
184
+ function sameOrDescendant(parent,candidate){
185
+ const relative=path.relative(parent,candidate);
186
+ return relative===''||Boolean(relative&&!relative.startsWith('..')&&!path.isAbsolute(relative));
187
+ }
188
+
189
+ function overlaps(left,right){
190
+ return sameOrDescendant(left,right)||sameOrDescendant(right,left);
191
+ }
192
+
193
+ async function canonicalDirectory(value,label){
194
+ if(typeof value!=='string'||!value.trim())fail(`${label} is required.`,ERROR_CODES.usage);
195
+ const requested=path.resolve(value);
196
+ let info;
197
+ try{info=await lstat(requested);}
198
+ catch(error){
199
+ if(error?.code==='ENOENT')fail(`${label} does not exist: ${requested}.`);
200
+ throw error;
201
+ }
202
+ if(info.isSymbolicLink()||!info.isDirectory())fail(`${label} must be a real directory.`);
203
+ return realpath(requested);
204
+ }
205
+
206
+ async function canonicalFutureDirectory(value,label){
207
+ if(typeof value!=='string'||!value.trim())fail(`${label} is required.`,ERROR_CODES.usage);
208
+ const requested=path.resolve(value);
209
+ let cursor=requested;
210
+ const missing=[];
211
+ while(true){
212
+ try{
213
+ const info=await lstat(cursor);
214
+ if(info.isSymbolicLink()||!info.isDirectory())fail(`${label} has a linked or non-directory ancestor.`);
215
+ const canonical=await realpath(cursor);
216
+ return path.join(canonical,...missing.reverse());
217
+ }catch(error){
218
+ if(error?.code!=='ENOENT')throw error;
219
+ const parent=path.dirname(cursor);
220
+ if(parent===cursor)throw error;
221
+ missing.push(path.basename(cursor));
222
+ cursor=parent;
223
+ }
224
+ }
225
+ }
226
+
227
+ export function validateNativeBuilder(provider){
228
+ const methods=['describe','doctor','prepare','authenticateToolchainReceipt','build','verify','run'];
229
+ if(!isObject(provider)||provider.protocol!==NATIVE_BUILDER_PROTOCOL
230
+ ||methods.some(method=>typeof provider[method]!=='function')){
231
+ fail(`Native builder must implement ${NATIVE_BUILDER_PROTOCOL} and its complete operation contract.`,ERROR_CODES.targetUnavailable);
232
+ }
233
+ return provider;
234
+ }
235
+
236
+ function validateTargetRequest(value){
237
+ assertOnlyKeys(value,new Set(['target','platform','architecture','format','signing']),'targetRequest');
238
+ const definition=NATIVE_TARGETS[value.target];
239
+ if(!definition)fail(`Unsupported native target: ${String(value.target)}.`,ERROR_CODES.targetUnavailable);
240
+ if(!definition.platforms.has(value.platform)||!definition.architectures.has(value.architecture)
241
+ ||value.format!==definition.format){
242
+ fail(`Target ${value.target} does not support ${value.platform}/${value.architecture}/${value.format}.`,ERROR_CODES.targetUnavailable);
243
+ }
244
+ assertOnlyKeys(value.signing,new Set(['mode','profileId']),'targetRequest.signing');
245
+ if(!Object.hasOwn(value.signing,'mode')||!Object.hasOwn(value.signing,'profileId')
246
+ ||value.signing.mode!==definition.signingMode
247
+ ||value.signing.profileId!==definition.signingProfileId){
248
+ const profileRequirement=definition.signingProfileId===null
249
+ ?'a null profileId'
250
+ :`profileId ${definition.signingProfileId}`;
251
+ fail(
252
+ `Target ${value.target} requires ${definition.format} with ${definition.signingMode} signing and ${profileRequirement}.`,
253
+ ERROR_CODES.targetUnavailable
254
+ );
255
+ }
256
+ return immutable({
257
+ target:value.target,
258
+ platform:value.platform,
259
+ architecture:value.architecture,
260
+ format:value.format,
261
+ signing:{mode:value.signing.mode,profileId:value.signing.profileId}
262
+ });
263
+ }
264
+
265
+ function releaseApp(receipt){
266
+ return isObject(receipt?.app)?receipt.app:null;
267
+ }
268
+
269
+ function releaseSummary(receipt){
270
+ const app=releaseApp(receipt);
271
+ if(!app||typeof app.id!=='string'||typeof app.version!=='string'
272
+ ||!SHA256_PATTERN.test(receipt.policySha256)
273
+ ||!SHA256_PATTERN.test(receipt.contentSha256)){
274
+ fail('The app release receipt is missing its verified identity.',ERROR_CODES.integrityFailed);
275
+ }
276
+ return immutable({
277
+ canonicalLocation:receipt.canonicalLocation,
278
+ policySha256:receipt.policySha256,
279
+ contentSha256:receipt.contentSha256,
280
+ fileCount:receipt.fileCount,
281
+ totalBytes:receipt.totalBytes
282
+ });
283
+ }
284
+
285
+ async function authenticateReleaseAuthority(
286
+ receipt,
287
+ releaseRoot,
288
+ signal,
289
+ expectedPackageConfig,
290
+ expectedDescriptor
291
+ ){
292
+ try{
293
+ return await authenticateAppReleaseAuthority(receipt,{
294
+ releaseRoot,
295
+ expectedPackageConfig,
296
+ expectedDescriptor,
297
+ signal
298
+ });
299
+ }catch(error){
300
+ if(error?.code===ERROR_CODES.cancelled)throw error;
301
+ throw new ArcaneError(
302
+ ERROR_CODES.integrityFailed,
303
+ `App release authentication failed: ${error instanceof Error?error.message:String(error)}`,
304
+ {cause:error}
305
+ );
306
+ }
307
+ }
308
+
309
+ async function authenticatedRelease(item,targetRequest,{signal}){
310
+ assertOnlyKeys(
311
+ item,
312
+ new Set(['appDescriptor','appReleaseRoot','appReleaseReceipt']),
313
+ 'application release input'
314
+ );
315
+ const presentedDescriptor=validateAppDescriptor(
316
+ item.appDescriptor,
317
+ {appId:item.appDescriptor?.id}
318
+ );
319
+ const root=await canonicalDirectory(
320
+ item.appReleaseRoot,
321
+ `Release root for ${presentedDescriptor.id}`
322
+ );
323
+ const authority=await authenticateReleaseAuthority(
324
+ item.appReleaseReceipt,
325
+ root,
326
+ signal,
327
+ projectPackageManifest(presentedDescriptor),
328
+ presentedDescriptor
329
+ );
330
+ const receipt=authority.receipt;
331
+ const descriptor=validateAppDescriptor(authority.descriptor,{
332
+ appId:presentedDescriptor.id
333
+ });
334
+ if(appDescriptorSha256(descriptor)!==authority.descriptorSha256){
335
+ fail('App release descriptor authority hash is invalid.',ERROR_CODES.integrityFailed);
336
+ }
337
+ projectNativeDescriptor(descriptor);
338
+ if(!descriptor.targets.includes(targetRequest.target)){
339
+ fail(`App ${descriptor.id} does not declare target ${targetRequest.target}.`);
340
+ }
341
+ const app=releaseApp(receipt);
342
+ if(app?.id!==descriptor.id||app?.version!==descriptor.version){
343
+ fail(`Release identity for ${descriptor.id} does not match its canonical descriptor.`,ERROR_CODES.integrityFailed);
344
+ }
345
+ return Object.freeze({
346
+ descriptor,
347
+ descriptorSha256:authority.descriptorSha256,
348
+ releaseRoot:root,
349
+ releaseReceipt:receipt,
350
+ readFile:(relativePath,{signal}={})=>readVerifiedAppReleaseFile(receipt,{
351
+ releaseRoot:root,
352
+ relativePath,
353
+ signal
354
+ }),
355
+ summary:immutable({
356
+ id:descriptor.id,
357
+ version:descriptor.version,
358
+ descriptorSha256:authority.descriptorSha256,
359
+ release:releaseSummary(receipt)
360
+ })
361
+ });
362
+ }
363
+
364
+ function assertDependencyClosure(application,dependencies){
365
+ const byId=new Map(dependencies.map(item=>[item.descriptor.id,item]));
366
+ if(byId.size!==dependencies.length)fail('Dependency releases must have unique app ids.');
367
+ if(byId.has(application.descriptor.id))fail('The selected app must not also be supplied as a dependency.');
368
+ const reachable=new Set();
369
+ const visiting=new Set();
370
+ function visit(descriptor){
371
+ if(visiting.has(descriptor.id))fail(`Bundled app dependency cycle includes ${descriptor.id}.`);
372
+ visiting.add(descriptor.id);
373
+ for(const id of descriptor.native.bundledApps){
374
+ const dependency=byId.get(id);
375
+ if(!dependency)fail(`Bundled app dependency ${id} is missing its verified release.`);
376
+ if(visiting.has(id))fail(`Bundled app dependency cycle includes ${id}.`);
377
+ if(!reachable.has(id)){
378
+ reachable.add(id);
379
+ visit(dependency.descriptor);
380
+ }
381
+ }
382
+ visiting.delete(descriptor.id);
383
+ }
384
+ visit(application.descriptor);
385
+ const extra=[...byId.keys()].filter(id=>!reachable.has(id));
386
+ if(extra.length)fail(`Unrequested dependency releases were supplied: ${extra.sort().join(', ')}.`);
387
+ }
388
+
389
+ function toolchainSummary(receipt,toolchainRoot){
390
+ if(!isObject(receipt)||receipt.canonicalLocation!==toolchainRoot
391
+ ||typeof receipt.kind!=='string'||!receipt.kind
392
+ ||typeof receipt.version!=='string'||!receipt.version
393
+ ||typeof receipt.protocolVersion!=='string'||!/^arcane\/[1-9][0-9]*$/u.test(receipt.protocolVersion)
394
+ ||!SHA256_PATTERN.test(receipt.contentSha256)){
395
+ fail('The native builder returned an invalid authenticated toolchain receipt.',ERROR_CODES.integrityFailed);
396
+ }
397
+ const features=canonicalContractList(receipt.features,'toolchainReceipt.features',{maximum:256});
398
+ const supportedCapabilities=canonicalContractList(
399
+ receipt.supportedCapabilities,
400
+ 'toolchainReceipt.supportedCapabilities'
401
+ );
402
+ const supportedMethods=canonicalContractList(receipt.supportedMethods,'toolchainReceipt.supportedMethods');
403
+ return immutable({
404
+ kind:receipt.kind,
405
+ version:receipt.version,
406
+ protocolVersion:receipt.protocolVersion,
407
+ features,
408
+ supportedCapabilities,
409
+ supportedMethods,
410
+ canonicalLocation:receipt.canonicalLocation,
411
+ contentSha256:receipt.contentSha256
412
+ });
413
+ }
414
+
415
+ function providerGenerationSummary(value,{optional=false}={}){
416
+ if(value===undefined&&optional)return null;
417
+ if(!isObject(value)
418
+ ||value.kind!=='arcane-native-provider-generation'
419
+ ||!SHA256_PATTERN.test(value.generationSha256)
420
+ ||!SHA256_PATTERN.test(value.contentSha256)
421
+ ||!Number.isInteger(value.moduleCount)
422
+ ||value.moduleCount<1
423
+ ||value.moduleCount>128){
424
+ fail(
425
+ 'Native provider generation evidence is invalid.',
426
+ ERROR_CODES.integrityFailed
427
+ );
428
+ }
429
+ return immutable({
430
+ kind:'arcane-native-provider-generation',
431
+ generationSha256:value.generationSha256,
432
+ contentSha256:value.contentSha256,
433
+ moduleCount:value.moduleCount
434
+ });
435
+ }
436
+
437
+ function assertProviderGenerationBinding(summary,provider,label){
438
+ if(!summary)return;
439
+ const actual=providerGenerationSummary(provider?.providerGeneration,{optional:true});
440
+ if(!actual||JSON.stringify(actual)!==JSON.stringify(summary)){
441
+ fail(
442
+ `${label} belongs to a different native provider module generation.`,
443
+ ERROR_CODES.integrityFailed
444
+ );
445
+ }
446
+ }
447
+
448
+ function assertExpectedPlanBinding(state,{expectedNativeBuilder,expectedTarget}={}){
449
+ assertProviderGenerationBinding(
450
+ state.providerGeneration,
451
+ state.provider,
452
+ 'Native build plan'
453
+ );
454
+ if(expectedNativeBuilder!==undefined){
455
+ assertProviderGenerationBinding(
456
+ state.providerGeneration,
457
+ expectedNativeBuilder,
458
+ 'Expected native builder'
459
+ );
460
+ }
461
+ if(expectedNativeBuilder!==undefined&&state.provider!==expectedNativeBuilder){
462
+ fail('Native build plan belongs to a different paired provider.',ERROR_CODES.integrityFailed);
463
+ }
464
+ if(expectedTarget!==undefined&&state.plan.targetRequest.target!==expectedTarget){
465
+ fail(
466
+ `Native build plan target ${state.plan.targetRequest.target} does not match ${expectedTarget}.`,
467
+ ERROR_CODES.integrityFailed
468
+ );
469
+ }
470
+ }
471
+
472
+ async function authenticateState(state,{expectedNativeBuilder,expectedTarget,signal,onEvent}={}){
473
+ throwIfAborted(signal);
474
+ assertExpectedPlanBinding(state,{expectedNativeBuilder,expectedTarget});
475
+ const authenticatedToolchain=await state.provider.authenticateToolchainReceipt(
476
+ state.toolchainReceipt,
477
+ {toolchainRoot:state.toolchainRoot,signal,onEvent}
478
+ );
479
+ const summary=toolchainSummary(authenticatedToolchain,state.toolchainRoot);
480
+ if(JSON.stringify(summary)!==JSON.stringify(state.plan.toolchain)){
481
+ fail('The native toolchain identity changed after plan creation.',ERROR_CODES.integrityFailed);
482
+ }
483
+ for(const item of [state.application,...state.dependencies]){
484
+ assertNativeToolchainCompatibility({
485
+ appDescriptor:item.descriptor,
486
+ toolchainReceipt:summary,
487
+ minimumCoreVersion:state.plan.minimumCoreVersion
488
+ });
489
+ }
490
+ await authenticateReleaseAuthority(
491
+ state.application.releaseReceipt,
492
+ state.application.releaseRoot,
493
+ signal,
494
+ projectPackageManifest(state.application.descriptor),
495
+ state.application.descriptor
496
+ );
497
+ for(const dependency of state.dependencies){
498
+ await authenticateReleaseAuthority(
499
+ dependency.releaseReceipt,
500
+ dependency.releaseRoot,
501
+ signal,
502
+ projectPackageManifest(dependency.descriptor),
503
+ dependency.descriptor
504
+ );
505
+ }
506
+ const outputRoot=await canonicalFutureDirectory(state.outputRoot,'Native output root');
507
+ if(outputRoot!==state.outputRoot)fail('The native output root identity changed after plan creation.',ERROR_CODES.integrityFailed);
508
+ return state.plan;
509
+ }
510
+
511
+ export async function createNativeBuildPlan({
512
+ nativeBuilder,
513
+ toolchainRoot,
514
+ toolchainReceipt,
515
+ appReleaseRoot,
516
+ appReleaseReceipt,
517
+ appDescriptor,
518
+ dependencyReleases=[],
519
+ providerGeneration,
520
+ minimumCoreVersion,
521
+ protectedRoots=[],
522
+ outputRoot,
523
+ targetRequest,
524
+ signal,
525
+ onEvent
526
+ }={}){
527
+ throwIfAborted(signal);
528
+ await onEvent?.(Object.freeze({type:'native.plan.started'}));
529
+ const request=validateTargetRequest(targetRequest);
530
+ const provider=validateNativeBuilder(nativeBuilder);
531
+ const generation=providerGenerationSummary(
532
+ providerGeneration===undefined?provider.providerGeneration:providerGeneration,
533
+ {optional:true}
534
+ );
535
+ assertProviderGenerationBinding(generation,provider,'Native build plan');
536
+ const canonicalToolchainRoot=await canonicalDirectory(toolchainRoot,'Native toolchain root');
537
+ const authenticatedToolchain=await provider.authenticateToolchainReceipt(
538
+ toolchainReceipt,
539
+ {toolchainRoot:canonicalToolchainRoot,signal,onEvent}
540
+ );
541
+ const authenticatedToolchainSummary=toolchainSummary(authenticatedToolchain,canonicalToolchainRoot);
542
+ const application=await authenticatedRelease({
543
+ appDescriptor,
544
+ appReleaseRoot,
545
+ appReleaseReceipt
546
+ },request,{signal});
547
+ if(!Array.isArray(dependencyReleases)||dependencyReleases.length>64){
548
+ fail('dependencyReleases must be an array with at most 64 entries.');
549
+ }
550
+ const dependencies=[];
551
+ for(const dependency of dependencyReleases){
552
+ dependencies.push(await authenticatedRelease(dependency,request,{signal}));
553
+ }
554
+ assertDependencyClosure(application,dependencies);
555
+ dependencies.sort((left,right)=>left.descriptor.id.localeCompare(right.descriptor.id,'en'));
556
+ const requiredCoreVersion=highestCoreMinimum([
557
+ minimumCoreVersion,
558
+ application.descriptor.requirements.minimumCoreVersion,
559
+ ...dependencies.map(item=>item.descriptor.requirements.minimumCoreVersion)
560
+ ]);
561
+ for(const item of [application,...dependencies]){
562
+ assertNativeToolchainCompatibility({
563
+ appDescriptor:item.descriptor,
564
+ toolchainReceipt:authenticatedToolchainSummary,
565
+ minimumCoreVersion:requiredCoreVersion
566
+ });
567
+ }
568
+
569
+ const canonicalOutputRoot=await canonicalFutureDirectory(outputRoot,'Native output root');
570
+ const protectedPaths=[
571
+ canonicalToolchainRoot,
572
+ application.releaseRoot,
573
+ ...dependencies.map(item=>item.releaseRoot)
574
+ ];
575
+ if(!Array.isArray(protectedRoots))fail('protectedRoots must be an array.');
576
+ for(const [index,root] of protectedRoots.entries()){
577
+ protectedPaths.push(await canonicalDirectory(root,`Protected source root ${index}`));
578
+ }
579
+ const conflict=protectedPaths.find(root=>overlaps(root,canonicalOutputRoot));
580
+ if(conflict)fail('Native output root must not overlap a toolchain, source, or verified release root.');
581
+
582
+ const plan=immutable({
583
+ protocol:NATIVE_BUILD_PLAN_PROTOCOL,
584
+ generation:randomBytes(16).toString('hex'),
585
+ ...(generation?{providerGeneration:generation}:{}),
586
+ toolchain:authenticatedToolchainSummary,
587
+ minimumCoreVersion:requiredCoreVersion,
588
+ app:application.summary,
589
+ dependencies:dependencies.map(item=>item.summary),
590
+ targetRequest:request,
591
+ outputRoot:canonicalOutputRoot,
592
+ operations:[
593
+ 'authenticate-toolchain',
594
+ 'authenticate-app-release',
595
+ 'authenticate-dependency-releases',
596
+ 'build-selected-target',
597
+ 'verify-native-artifact'
598
+ ]
599
+ });
600
+ issuedPlans.set(plan,Object.freeze({
601
+ plan,
602
+ provider,
603
+ providerGeneration:generation,
604
+ toolchainRoot:canonicalToolchainRoot,
605
+ toolchainReceipt,
606
+ minimumCoreVersion:requiredCoreVersion,
607
+ application,
608
+ dependencies:Object.freeze(dependencies),
609
+ outputRoot:canonicalOutputRoot,
610
+ targetRequest:request
611
+ }));
612
+ await onEvent?.(Object.freeze({type:'native.plan.completed',appId:application.descriptor.id,target:request.target}));
613
+ return plan;
614
+ }
615
+
616
+ export async function authenticateNativeBuildPlan(plan,{
617
+ expectedNativeBuilder,
618
+ expectedTarget,
619
+ signal,
620
+ onEvent
621
+ }={}){
622
+ const state=issuedPlans.get(plan);
623
+ if(!state)fail('Native build plan was not issued by this SDK process.',ERROR_CODES.integrityFailed);
624
+ return authenticateState(state,{expectedNativeBuilder,expectedTarget,signal,onEvent});
625
+ }
626
+
627
+ export async function executeNativeBuildPlan(plan,{
628
+ expectedNativeBuilder,
629
+ expectedTarget,
630
+ signal,
631
+ onEvent
632
+ }={}){
633
+ const state=issuedPlans.get(plan);
634
+ if(!state)fail('Native build plan was not issued by this SDK process.',ERROR_CODES.integrityFailed);
635
+ throwIfAborted(signal);
636
+ if(attemptedPlans.has(plan)){
637
+ fail(
638
+ 'Native build plan generation has already been attempted; create a new plan before retrying.',
639
+ ERROR_CODES.integrityFailed
640
+ );
641
+ }
642
+ attemptedPlans.add(plan);
643
+ await onEvent?.(Object.freeze({type:'native.build.started',appId:plan.app.id,target:plan.targetRequest.target}));
644
+ await authenticateState(state,{expectedNativeBuilder,expectedTarget,signal,onEvent});
645
+ const result=await state.provider.build({
646
+ toolchainRoot:state.toolchainRoot,
647
+ toolchainReceipt:state.toolchainReceipt,
648
+ appReleaseRoot:state.application.releaseRoot,
649
+ appReleaseReceipt:state.application.releaseReceipt,
650
+ readAppReleaseFile:(relativePath)=>state.application.readFile(relativePath,{signal}),
651
+ appDescriptor:state.application.descriptor,
652
+ descriptorSha256:state.application.descriptorSha256,
653
+ dependencyReleases:state.dependencies.map(item=>Object.freeze({
654
+ appReleaseRoot:item.releaseRoot,
655
+ appReleaseReceipt:item.releaseReceipt,
656
+ readFile:(relativePath)=>item.readFile(relativePath,{signal}),
657
+ appDescriptor:item.descriptor,
658
+ descriptorSha256:item.descriptorSha256
659
+ })),
660
+ outputRoot:state.outputRoot,
661
+ targetRequest:state.targetRequest,
662
+ signal,
663
+ onEvent
664
+ });
665
+ if(!isObject(result)||!result.artifactReceipt){
666
+ fail('Native builder did not return an artifact receipt.',ERROR_CODES.integrityFailed);
667
+ }
668
+ await onEvent?.(Object.freeze({
669
+ type:'native.build.committed',
670
+ appId:plan.app.id,
671
+ target:plan.targetRequest.target
672
+ }));
673
+ await onEvent?.(Object.freeze({
674
+ type:'native.verify.started',
675
+ appId:plan.app.id,
676
+ target:plan.targetRequest.target
677
+ }));
678
+ const artifactVerification=await state.provider.verify({
679
+ toolchainRoot:state.toolchainRoot,
680
+ toolchainReceipt:state.toolchainReceipt,
681
+ artifactReceipt:result.artifactReceipt,
682
+ outputRoot:state.outputRoot,
683
+ targetRequest:state.targetRequest,
684
+ signal,
685
+ onEvent
686
+ });
687
+ if(!isObject(artifactVerification)||artifactVerification.verified!==true){
688
+ fail('Native builder did not verify the built artifact.',ERROR_CODES.integrityFailed);
689
+ }
690
+ throwIfAborted(signal);
691
+ await onEvent?.(Object.freeze({
692
+ type:'native.verify.completed',
693
+ appId:plan.app.id,
694
+ target:plan.targetRequest.target
695
+ }));
696
+ await onEvent?.(Object.freeze({type:'native.build.completed',appId:plan.app.id,target:plan.targetRequest.target}));
697
+ return {...result,artifactVerification};
698
+ }